Repository navigation
Releases: ThinkFlowLab/FlyBot
Releases · ThinkFlowLab/FlyBot
Release list
FlyBot v0.2.19
Changed
- Project board: a role-pool entry in the team block reads
@idbeside its rank instead of a Dossier control —project-teamanswers rows keyed by the Project's own Bots, so the old pool click opened a dossier the host never sent; the Chief and my-agent rows keep theirs (#1892).
Fixed
- The Project memory box now counts its 600-character bound in characters rather than UTF-16 units, so a note of 600 emoji — 1,200 units — is saved instead of refused as A Project memory entry is at most 600 characters, and 601 characters is still refused in both writers (#1886).
- The Project settings memory box warns about its bound before the click: a
N/600counter under the field counts code points like the host, a 601st code point turns the counter and the Save button into their over-limit state, and an empty or whitespace-only box disables Save, so neither state callsproject-memory-writeor writes anything (#1887). - CI now runs
node scripts/ds-bot-patches.mjs canonical, which fails whengeneratewould rewritepatches/ds-bot/:checkproves the committed series applies and reproducescore/, not that it is the split the generator writes, so a drifted series landed green between #1883 and #1889.npm run verifyruns the same gate. (#1890) - Host character limits count code points, so a character outside the BMP costs one instead of two (#1895): a memory entry at 300, a
MEMORY.mdat 4,000 and its size note, a scheduled task's name at 120, a Bot-to-Bot message at 20,000, and the Project memory entry at 600 in both writers now measure what a reader counts, and a refusal prints that code-point number. - The Project board's wait label now carries seconds under a minute, so a card that waited 40 seconds reads
40sinstead of0m; the three duration metrics (PR merge time, issue close time, task completion) use the same label, and a span over zero never floors to0s. Unknown and negative spans still render nothing. (#1896) - An incident entry's
trigger,wrongandrightare bounded at 600 characters counted as code points, not UTF-16 units (#1899): 300 emoji no longer fill the 600 budget, a 600-character emoji entry is stored rather than refused as 1,200 units, and an entry already on disk that is over 600 UTF-16 units but within 600 characters loads again through the same check on the read path. - Project board: the five-second refresh no longer re-reads the team dossier —
project-teamis read once when the board opens and handed back to each poll, so an open board stops re-reading persona and memory files (about 192 KB a minute) for an answer only an edit changes; a dossier that fails to read now leaves the rows, the done group and the tasks on screen with an empty team block instead of showing "No Project yet" (#1900). - The task board's typed question and answer (2,000) and its handoff, evidence and lease bounds (300, 200, 200) now count characters rather than UTF-16 units (#1901), so a 2,000-character emoji question is accepted instead of refused as 4,000.
read_own_chatnow says what a cap left out instead of reading as the whole chat: a search that fills its 20 hits ends withN more not shown; search with narrower words, or read around one of these anchors., and a bare read of a part with more than 12 messages is headednewest 12 of 15, oldest first. The caps stay; exactly at the cap nothing is added. (#1906)- A clipped or excerpted line no longer ends on half of an emoji (#1907): the host cuts
read_own_chat, checkpoint and handoff lines, the memory review input and the error sentences on a code point boundary, so what a client receives is the whole character instead of a replacement character. - Project board: a refused
project-teamread at open is no answer yet, not an empty team — the next five-second poll asks again, the block fills when the answer lands, and it stays frozen for the rest of the open; before this the refusal was normalised to{ rows: [] }and served by every later poll (#1910). - A decision or a meeting archive no longer stores half of an emoji (#1912): the host's one
cuthelper stops a bounded prefix before the surrogate pair it would split, and every archive writer now uses it instead ofslice(0, max)—decisions.jsonl,decisions/<date>-<slug>.md, and the claim line, diagnostics report, activity-feed label and browser text beside them — so the stored record round-trips through UTF-8 as the whole character instead of U+FFFD. - The Project board's Dossier panel now re-reads the team dossier when it opens, so a memory head written
after the board opened is one click away instead of stale until the board is closed and reopened. A
refused refresh keeps the rows the panel already had, and the five-second poll still does not read the
dossier. - Eight more places stopped storing or showing half of an emoji (#1916): the claim cue's fallback reference, the task lead's name in a member's prompt, a failed group Session's error text,
read_own_chat's query and anchor, an open page's address and title, a diagnostics browser-log line, and a friend's Bot name now cut on a code point boundary. The rule itself moved tocore/src/shared/text-boundary.js, so the shared label code and the host'scutuse one implementation instead of two. - The memory review now keeps whole characters in what it shows the model and writes to the log (#1918): the two bounds in
core/src/host/memory.js— the 800-unit context line and the 3,000-unit two-ended user line — go through the shared boundary rule, so a cut that lands inside an emoji steps back instead of leaving half a pair, and the tail it keeps is never longer than the budget it had. The exportedclipincore/src/host/text.jsalso makes its reason optional: a two-argument call now reads… (cut)instead of the literal… (cut; undefined), while a call that passes a reason keeps… (cut; <reason>)byte for byte. - The diagnostics report's stack summary stopped showing half of an emoji (#1919): the
<details><summary>Stack: …line now cuts the message on a code point boundary, so the character the 80-unit bound would split is left out of the summary entirely and the line shows a whole-character prefix instead of ending in U+FFFD. - A friend's Bot name no longer shows half of an emoji (#1923): the published name stored from the roster, the group member line, the room label
othersInRoomreturns and the envelope's name, handle, ownerName and roster fields now cut on a code point boundary, so such a value that straddles its 60 (or 40) cap keeps the whole character instead of U+FFFD. The envelope's message body and its fitTransport shrink loop are untouched. - A room-share envelope keeps whole characters in the text it copies to another owner's FlyBot (#1928): the history line a join copies (bounded at 2,000 units on both the host that builds the envelope and the guest that copies it) and a line fitted to Bot Social's 4,000-character transport limit now stop before a character that straddles their bound instead of leaving half of it, so a guest no longer reads U+FFFD where the sender typed an emoji — a family emoji fitted to the transport reproduces the old break. The 18,000-unit bound on a line envelope's body goes through the same rule for consistency; it is not reachable on its own because the transport fit already shortens the body to 4,000.
- The host's own bounded lines no longer end in half of an emoji (#1930): the busy acknowledgment's 600-unit task text and 1,500-unit message text,
cleanName's 60-unit peer name (the one that lands infenceUntrusted'sfrom="…"attribute), thelist_botsgroup line's 100-unit notice and 120-unit job line, the group answer echo at 300 and a rework card's note at 200 now cut on a code point boundary, so a value whose cap falls inside a surrogate pair keeps the whole character instead of U+FFFD. Every cap is unchanged. - A join to a shared room keeps the group notice even when the room's Bot roster has to shrink to fit (#1932): the roster used to be trimmed after the notice, and the notice was cut by the whole envelope's overflow, so a room with enough Bots emptied it and a guest never heard an edited or cleared notice — an explicit clear (an empty notice) was dropped along with it. The roster now gives its rows first, the notice is cut only by what is left over once the roster cannot give more, and a present empty notice arrives as the clear it is. No limit changes: a join still fits Bot Social's 4,000-character transport.
- A joined room's notice and the pane line copied from another owner's FlyBot keep whole characters (#1933; #1928 covered the envelopes, and these are the two host-side readers it left on a raw code-unit cut): the guest bounds the notice it stores at its own 2,000 u...
FlyBot v0.2.18
Added
- We worked a workflow (a workflow now belongs to a Project: its definition carries projectId, the run is bound when it starts, and Settings shows the Project) — #1856.
- Every Project now gets its own Chief, created with the Project: the record carries
chief: { botId, sessionId, createdAt }, the Chief Bot carries the Project binding, andproject-deleteretires it with the Project. It speaks for that one Project only, never for the user's main Chief (#1859). - A Project's page now opens with its Project team: the Project's own Chief, the assistant that speaks for you there, and the role pool (Owner, Maintainers, Members) together with the rank you hold and the ranks you may switch to. Each of the two Bots opens that chat straight from the board, and a Project that has no Chief yet keeps the row and reads Not set instead of hiding it (#1860).
- A meeting can now record who a Project Chief or an owner's agent speaks for: positions carry
speaksFor, the archived decision file names the proxy beside that position, and aseveredecision is refused when one side rests only on a proxy whose person has neither taken a position nor been recorded silent (#1861). - Workflows can now record a shadow verdict for a handoff before anything depends on it: the pool contract of #1853 runs on the handoff a caller passes to
assign, and the answer lands on the run record asshadow: { verdict, code?, reason? }and as oneshadow: <verdict>line in the run's own stage log. Nothing reads it — the run, the card and the pull request fast lane behave exactly as they did, which is the point of the shadow step (#1862). - The Project board shows each Project Bot's dossier — persona, memory head and topic names, and attached skills — from one read-only
project-teamread, with aDossiercontrol on the Chief, my-agent and role-pool rows; the owner's own Bot chat header now has an entry that opens the Project assistant's chat, without un-hiding it in the global Bot list (#1864).
Fixed
- Settings → Workflows follows a live run instead of freezing at the moment the page opened: while any workflow has work running the page re-reads
workflow/stateevery 2 seconds — the room drawer's own beat — so a step that goes running → done changes its chip without reopening the page, an edit in progress suspends the refresh, and the page stops asking when the last run finishes or it is closed (#1810). - The Project board's queue rows beside the columns now read 运行中 for a live claim and 已停滞 for a stalled one in the Chinese interface, instead of printing the host's lowercase
running/stalledids (#1813). - The Project role switch answered
bot/not-found: Unknown endpoint project-roleon a host with a
member session:channel.jsdispatched it andstore.jsimplemented it, but authz's explicit
TASK_ENDPOINTSlist never named it, so the gate refused before the handler ran. It is listed
there now, with a guard test that holds every endpoint the channel dispatches to a gate answer
that is not "unknown endpoint". - A Project role name is now matched against the rank table's own keys instead of the prototype chain:
toString,__proto__andconstructoranswerunknown-roleand leave the record untouched, where they used to demote a Maintainer to Member and report success (#1838). - A Main Bot can no longer rewrite, retire or stop another owner's Bot. On a deployment where the owner rule is enforced,
update_bot,archive_botandstop_botnow accept a target only when it belongs to the Bot's own owner or is granted to that owner — the same rulelist_botsand the HTTP side already kept — so the instructions, retirement and running turns of another owner's Bot stay untouched (#1844). The three refusals also read one sentence,No Bot named <name>., whether the Bot is missing or foreign, and no longer name the rest of the team. Single-owner installs behave exactly as before, and the refusal text is aligned with no other behaviour change. - Project settings now shows the caller's own Pause the daily digest switch: the panel read a field the Project record never carried, so a digest its owner had paused always opened unticked, and pressing Save on that row sent
off: falseand quietly resumed it the next morning.project-readnow answers the caller's own assistant record (assistantMine) beside the Project, the panel reads the box from it, other owners never see each other's switch, and a Save that changes nothing no longer writes or sendsschedule-resume(#1845). - Project Assistant: the 09:00 digest now reads the Project's own board instead of an actor-less one, so a signed-in install no longer gets "no change since the previous digest" every day and the archived watermark stops hiding finished cards (#1846).
- The Project memory box now refuses what the Project Assistant's own writer always refused: an empty entry is answered with A Project memory entry needs text and one over 600 characters with A Project memory entry is at most 600 characters, in the interface language in the panel, and neither writes a row or touches the Project. Before this the endpoint stored a blank entry, or cut a long one to 600 characters, and still reported success (#1847).
- The Project board's running table now reads the cards of the board it belongs to instead of the whole deployment's task store, so another Project's claimed card no longer hands out its title and holder; the same list is carried by every branch of the metrics read, so the table stays populated when the GitHub source is unavailable or fails, and the done-task median is this board's cards too (#1848); a five-minute cache entry shared by two Projects on the same repository no longer hands one Project's done-task median to the other (#1880).
- An unknown
timeZonein a Project board read no longer takes the whole board down: the zone is resolved once inproject-metrics.js, an unknown name falls back to the namedUTC, and the answer says which zone it used (timeZone), while rows, queues and decisions keep answering and a valid zone reads exactly as before (#1849). - Expanding Projects in the left sidebar now lists the Projects you can see, each with the role you hold there, and a right-click can switch that role: the sidebar had been asking the host for an endpoint that does not exist, so the list was empty on every deployment. The duplicate Project board button is gone from the Tasks dialog, so a Project opens from the sidebar only (#1854).
- The Project board no longer opens on a wall of finished work: a Project with months of history shows the cards still in flight, and every finished card lives in one Done cards group that starts collapsed, with the header still counting them (
Done: 191). Only the finished column left the rows — a rework, blocked or waiting card is still a row, and the queue groups, the row shape and the metrics are unchanged (#1855). - A workflow no longer ends early because an output value merely contains
open-pr: a declared OUTPUT now countsopen-pr(andtip-unchanged) only as a whole word, so a triage brief that namesdata-open-projectlets the run continue to Claim instead of finishing with every successor still queued (#1866). - A Workflow step that runs past its lane's
stepTimeoutMinutesis now reported as having reached its deadline (step deadline reached (45 min)) instead ofno reply for 45 minutes, so a step that was still working no longer reads as a Bot that went silent; the stage line andrun.failurecarry that wording, and thedefine_workflowtool acceptsstepTimeoutMinutes(1–120) so a new lane does not silently inherit the 45-minute default (#1868). workflow/assignnow delivers the brief it is given. The brief is stored on the task, kept on a parked card (soworkflows.jsonandworkflow/stateshow what the node was actually told) and handed to the node as its instruction, with the title used only when no brief is given;run_workflowtreats a brief the same way and takes its title from the brief's first line, and a fieldassigndoes not read is now named back inignoredinstead of being dropped in silence (#1869).- The Project board's task-completion median now counts a card the way the task store finishes it —
status: 'accepted', timed from the last terminalactors[]entry — instead of thecompletedAtfield the store never writes, so the number stops reading—on a real deployment (#1884).
FlyBot v0.2.17
Added
- Projects gain the three roles the governance matrix names, and a way in: the Owner is derived from
ownerUserIds(an older record withoutrolesreads owner-only),project-rolesets a Maintainer or Member only after the host re-checks who may re-role whom, refusingnot-owner/cannot-self-promote/last-ownerwithout touching the record. The sidebar's Projects row now expands to the Projects you can see with your role there and opens that board straight, and its right-click My role menu offers only the ranks the host says you may switch to. - Project board: seven metrics (today's opened/merged PRs and issues, 7-day medians) and a running-task table, read from GitHub REST plus the local tasks behind a 5-minute cache. A missing credential or a failed read renders `—`, never `0`.
- Decision cards carry a level (ordinary, major, severe) and the people they are addressed to; a severe card never advances on its own and says why. The Project board shows the level badge and recipients, and a card row names its claim holder and the user the Assistant speaks for.
Fixed
- A Bot Social read served from the channel answers only for the Bot the actor owns: a
body.botIdthe actor does not own is refused withbot/forbiddenbefore the handler registers that Bot's identity or syncs its grants (#1788). - A Project board lists a decision file archived in the Session's working folder, not only a file in the Project's own folder: the meeting record now names the folder it wrote to, and the board reads the union of the Project folder and each decided meeting's file (#1809).
- The Project board now reads itself again every five seconds while it is open: a lease that expires during the visit turns its card
stalledand a card that moves from waiting to in flight moves too, without closing and reopening the page; a poll that fails keeps the last good board on screen, and reads pause while the tab is hidden or a row's own board is open (#1814). - Retiring a Bot now disarms the tasks armed in the group Sessions it speaks through as well as those in its own chat; a group's own reminders are left untouched (#1816).
- The Project settings panel's Save memory button now saves: it sends one of the two memory kinds the host knows (the free text is recorded as a decision) instead of a
notekind the host refused, so the success line appears and the entry reaches the Project Assistant's memory. A refusal the panel can surface is shown in the interface language instead of the host's English sentence (#1820). - A line typed in a Bot's own chat while its turn is running now reaches that turn at its next step instead of waiting as a separate queued turn, so a mid-run correction changes what the Bot does next (#707).
FlyBot v0.2.16
Added
- Settings has a Projects page beside Bots and group chats (#1766): create a Project with a name and an optional working folder chosen from the folder picker, open its board from the row, and bind or unbind a Bot or a group. A Project-less board now offers the page that creates one instead of a dead end.
- The Project board now lists the Project's decisions beside its cards: every
decisions/*.mdfile in the Project's working folder with its date, title line and sha256, and the meetings that reacheddecided— a meeting still deliberating or blocked is listed apart and never as a decision. A folder with no decision file says which folder was read instead of drawing an empty box, and a decided meeting whose file was deleted stays listed with no path. - The Project board now shows the work beside its columns: cards waiting for a role (oldest wait first, with the role and how long), the member holding each card and whether its lease is live or stalled, and blocked cards with their one-line reason, plus waiting / in flight / blocked / done counts in the header. An unreadable queue reads "unknown", never 0 (#1768).
Fixed
- Retirement now reaches the host paths that resolved a Bot without
findTeamBot: a retired Bot takes no task, group turn, capability, memory write, new or resumed schedule, or held or restarted delivery, the task drive no longer wakes it, andarchive_botpauses the tasks armed on its own chat and fails the cards it still holds (#1709). - The Workflow stage drawer's Edit plane no longer squeezes a two-Bot parallel step: a pair the drawer's fitted plane left 4px apart is now drawn at the row width the Read-only Flow tab gives it, so the Reviewer and Tester cards keep their gap and neither card's out-port or remove button lands under its neighbour, while the parallel frame encloses both cards and the "Parallel branches · Merge after all pass" label stays on one line inside the 364px panel (#1738).
- A member keeps its one work slot free once its result is delivered for acceptance, so the next queued item is offered to it instead of stalling a one-member role; a
result-ready(or blocked) card no longer counts as work in progress (#1744). - Project board: the Project settings and Members buttons now open panels (#1749). Settings saves bindings, the Project Assistant's digest setting and its memory through the existing host write paths; the Members panel lists the Project's owners and the members its cards name, read-only for now.
- The Project board now opens as a real modal: a dimming scrim sits in front of the board and covers the shell, so the bot tree and workflows no longer read as extra board rows, and a click beside the board closes it (#1750).
- A Project whose assistant was created while DSH automation tasks were off kept
error: "Automation tasks are off in DSH"forever: switching automation back on left Scheduled tasks empty, and callingproject-createagain answeredcreated:falsewithout scheduling (#1751). The assistant now reconciles every Project whose assistant exists without ascheduleId— when the schedule service comes online and on a repeatedproject-create— writing the 09:00 Asia/Shanghai digest once and clearing the recorded error. - Settings → Workflows now fits the authored graph into the canvas instead of clipping it: one scale on a wrapper shrinks cards, the dashed parallel frame and the wires together (never past half size), and the frame is drawn around every member, so a parallel card the editor stored lower stays inside its branch box, its status chip is no longer cut to
Q…, and the Claim → 取证 wire ends inside the canvas instead of on the clip line (#1752). - The room's Flow panel now paints the last run's node statuses for an idle workflow, the same statuses Settings → Workflows draws, instead of showing every node as Queued; a workflow that never ran shows Idle in both views (#1753).
- The
meetingtool archives a decision that lists the cards it produced: the file now carries- Tasks:with the produced ids in order, every produced task is pre-flighted before anything is written (a refused or unauthorized task leaves no card, no temp file and no decision file behind), the day's second decision takes the next free name (…-2.md) instead of refusingexists, and a blocked meeting can be resumed with an answer and then decided, while a decided one stays closed (#1754). - The Project Assistant settings — the digest opt-out, the quiet window and the zone — can now be written by the Project's owner from the product:
project-assistant-owneris dispatched by the host channel and authorization-checked like the other Project writes, so a non-owner getsbot/forbiddeninstead ofUnknown endpoint(#1759). - A meeting that cannot archive its decision no longer leaves a card behind: the archive folder is claimed before any produced card is created, so an unwritable
decisions/folder fails the decide with no card, no decision file and the meeting still open, and an attempt that fails after the cards exist keeps their ids on the meeting record so a retry reuses that card instead of creating a second one (#1763). - Renaming a face-to-face room no longer fails with "A group chat needs at least two Bots": the host applies that floor only to a patch that actually changes membership, so a name, notice or reply-mode edit goes through in a face-to-face or inbound-share room while a normal room's membership patch still needs two Bots (#1764).
- Settings → Workflows no longer cuts off the left side of a graph that is too wide for the canvas: below the half-size floor the leftover width is reserved on the left of the stage instead of spilling out of both edges, so the leftmost card and the dashed frame are drawn where they can be seen and the rest scrolls to the right — a scroller cannot scroll left of zero, which is why those pixels could not be brought back before (#1769).
- A Workflow step that ends without printing a declared
OUTPUTits next gate reads now fails the step and the run instead of closing as✓ 完成: the downstream branch is held rather than skipped, and the run record names the step and the missing contract (Missing OUTPUT verdict), so "upstream emitted nothing" is no longer indistinguishable from "upstream emitted a non-PASS verdict" (#1773). - A meeting is now a record on disk instead of a decoration of the live process: every write of the
meetingtool (open,position,decide,block,resume) reaches the team file before the tool answers, so reopening the app shows the meeting in the state the caller was told — thecore-hostcontract that a meeting survives a restart now holds for every write verb. A decided meeting staysdecided— a retry made by a reader that saw it asdeliberatingis refused withclosedinstead of writing a seconddecisions/…-2.mdand a second card — and an open meeting keeps its recorded positions. A decision also saves the ids of the cards it produced before anything further is written, so a restart between the cards and the archived file keeps those cards instead of doubling them (#1774). - Fix the team run report printing
1000.0kfor a count between 999,950 and 999,999; it now carries into the next unit and reads1.0M(#1779). - The timeline report's
## Teamroll-up now names the same Bots its own per-Bot sections flag: a Bot whose failed turns began before the window (so it has more failures than turns inside it) appears underneeds a lookagain instead of being dropped while its section still reads3 of 3 turns failed(#1780). - Bot Social no longer prints
1024.0 KBfor a file just under a megabyte: a size that rounds up to 1024 KB is shown as1.0 MB(#1782). - The timeline panel no longer prints a duration its own unit cannot hold: a turn that took 59.999 s reads
1m 00sinstead of60 s, and 9.999 s reads10 sinstead of10.0 s(#1783). - A retired Bot is no longer billed a memory review or have its archived memory rewritten when another member's turn ends; a live lagging member is still reviewed (#1786).
- The timeline report no longer prints its own window backwards when the range crosses local midnight:
Window: 23:50–00:10and the one-line summary now read23:50–00:10 (+1 day), while a range inside one local day keeps exactly the text it had (#1787). - Settings → Workflows no longer draws a node the run skipped as Queued: the chip now says Skipped, the same word the Room's Flow panel uses for that node, so a run with an untaken branch no longer reads as stuck (#1789).
- A Bot retired with
archive_botnow leaves the tree panel the way it already left the si...
FlyBot v0.2.15
Added
- A task card now carries a typed contract:
taskClass(work,rework,release,triage,meeting),reworkOffor a card that fixes a rejected one,handoffnaming the roles it moved between and why,evidencefor the sha/PR/check pointers that prove the work, andleasefor the claim on it. Every field is validated on write and refused by name, a review rejection opens exactly one rework card beside the original instead of flipping its status, an expired lease returns a queued card to the queue while a live one refuses a second claim naming the holder, and the Project board reads the same row without a second copy. A card stored before this slice reads back unchanged (#1720). - Role queues now carry a stated cap (8 by default, override per graph), every claim runs in its own worker with no shared state, and one owner recovers an expired lease — once — recording it on the card's evidence.
- A Project board card now carries an
auditblock: the handoff, the evidence, the rework chain and the current lease rendered from the task record itself, with no new endpoint or page. A rework cycle is counted as one accepted delivery instead of two, so the counters no longer double after a rejection. - Meetings are now recorded in FlyBot: a meeting moves
open → deliberating → decided | blocked, requires a position from every invited participant and a named decision owner, archives onedecisions/<date>-<slug>.mdondecided(and nothing onblocked), and creates the follow-up tasks with that file as evidence (#1731). - Each owner of one Project now gets their own 09:00 digest, built from one board read with their own memory and their own decisions list, with a per-owner quiet window that holds a due digest and delivers it once when the window ends, an opt-out that stops generation and pauses the 09:00 task until it is turned back on, a refusal that names the boundary when one owner asks for another's memory, and failure isolation so one owner's broken render still leaves the other owner's digest delivered (#1733).
Fixed
- The Inbox no longer shows a task that was never started as "In progress", and no longer ages it from its creation: the Inbox and the Board now read one task row, so the status label and the counter agree and a card's age comes from the moment the row started.
FlyBot v0.2.14
Added
- A Bot that needs
PROJECT.md,ROADMAP.md,docs/governance.md, a decision file orLESSONS.mdcan now read it through theproject_statetool, which lists the Project State files of its working folder with a sha256 content hash, byte size and last-modified time, or returns one file whole up to 200,000 bytes. A missing file, an oversized file and a folder without.giteach answer with the reason instead of an empty string (#1702). - A Project now has a board of its own: one read-only view over the work under it, grouped by where it lives (Bots, groups, workflow runs), showing each row's column, assignee and the run and node it came from, with every row opening the task it comes from. A Project with nothing under it says so instead of failing. Only the Project's owner sees the management controls, and the host refuses a binding or a Project change from anyone else rather than relying on the page to hide a button. Open the board at
#project=<id>(#1710). - A Main Bot can retire a Bot with
archive_bot: it leaves the tree (freeing amaxChildrenslot) and the default Bot list, while its chats, memory and history stay readable and it can be restored. Deleting a Bot stays the owner's own action in the UI (#638).
Changed
- The workflow stage drawer now looks like the rest of FlyBot: the Bot details drawer's title, role line and pill tabs, soft pill buttons, chat-bubble node cards and run card, the system font stack, and accent tints mixed from the theme's
--bt-accentinstead of a fixed blue, so every theme recolours it (#1654). - A Bot that is busy now acknowledges a new message with one short, natural sentence written by a quick model call, in the message's language, instead of a templated line that quoted a truncated piece of the message; when the model does not answer within 3 seconds only the pending dots show (#1665).
Fixed
- A Bot turn the provider refused with a requests-per-window
429(Limit type: requests,Remaining: 0) is now asked again after a backoff — 2 s, 8 s and 30 s — instead of spending the turn, and the group round or sender keeps waiting for the same exchange. A refusal that outlasts every attempt ends the turn once, in the visible failure line the chat already draws, naming the provider text. A budget or quota refusal is not asked again (#1308). - The new-task form in Scheduled tasks no longer opens with the first Bot already chosen: the Send to picker starts on "Pick a Bot or group chat", and Create task stays disabled until a Bot or group chat is picked, so a task can no longer be created for whoever sorts first on one click (#1334).
- Retrying a Workflow task no longer leaves two indistinguishable run rows: every attempt keeps its row, the older finished attempt is marked
superseded by <run>and the newer oneretry of <run>, and the room opens on the newest attempt for that task. Two live attempts for one task are both shown unmarked. (#1507) - A human message sent while a foreground tool or job wait has been open for more than 60 seconds now stops that turn the way Stop does and keeps the message in the inbox, so the line is claimed at once instead of waiting for the command to finish. Shorter waits, finished waits, group rooms and idle Bots are untouched (#1509).
- A run that ended early on
needs-human,already-claimed,tip-unchangedoropen-prno longer reads as a failed step: the run log ends with⏹ 提前结束 · <reason>and still quotes the Bot's reply, instead of saying the node failed and stopped there (#1577). - A Workflow node that names no model now runs on the profile default whichever surface started the run, so the same graph no longer changes model, cost and tool capability with whoever pressed start; the connector ceiling and the refusal of a run started by a friend's Bot still hold (#1582).
- The face-to-face room no longer closes itself a few seconds after
face-start: one snapshot that lists no peer no longer counts as every other home leaving, so the room ends only when two polls in a row find no other home, or the server sends the leave event. Seats a room has listed are remembered per room, so a dropped peer no longer looks like a room of one (#1606). - The room approval dock no longer tells you another owner is waiting when the card is yours: the "Waiting for the owner's approval" card (no buttons) now waits until both approval lists it polls have answered and neither holds a card for this room, so a list that is one 5 s poll behind leaves the dock quiet instead of hiding your own Approve / Reject buttons (#1607).
- An attempt a Workflow graph edit stops no longer loses its
superseded bymarker: the history row that edit writes now carries the id of the run it ended, so once the same task is handed to the workflow again the Runs tab names the stopped attempt as superseded by the retry. A legacy demo record that stores a run id keeps it in the migrated row too (#1611). - A face room no longer closes itself when the member list is still catching up: the "everyone left" count advances only on a poll, so the
face-startresponse no longer spends its one poll of slack and a single lagging poll after Enter chat keeps the room open. The second agreeing poll, or a snapshot carrying the leave, still ends it (#1612). - A run Bot the host could not remove after a Workflow run ended, was stopped, or the app closed is no longer forgotten: its id stays on the run record, so the next load retries the removal instead of leaving a hidden Bot that no screen can find again. (#1616)
- Workflow room Runs tab keeps the HH:MM clock in its own column with a gap before the status chip, and a Failed row without a host reason shows the Failed chip once instead of also painting a clipped red F.
- Deleting a workflow, and a graph edit that ends a running node, no longer drop a run Bot's id before the release that can fail: an id the host would not remove stays on the archived run or in the store's own
orphanslist, and the next load's reap retries it instead of leaving a hidden Bot no state names. - After a message is sent on DeepSeek Harness 0.2.1, the Bot avatar and shimmering status line under the transcript show again: the activity row finds
RunningStatuseven when it sits inside theconversation.chat.flowslot (#1626). - The busy-ack bubble no longer repeats one canned sentence. While a Bot is working, a new message gets a short one-line ack composed on the spot from what you wrote and what the Bot is doing (e.g. 「看到你说「帮我改周报」了,我正在跑命令,弄完马上接上。」), in the language of your message, with varied wording that never repeats the previous ack — still instant, no extra model call (#1629).
- Deleting a Workflow no longer leaves its task claim behind, so the task matches again on the next board write instead of failing with
No such workflow; a store that already holds a claim for a deleted Workflow drops it when the plugin loads. (#1632) - The busy ack now quotes your own message exactly: typing
{d}or{t}in a line no longer makes the ack swap in the running tool's words (or drop it) (#1634). - An opened thinking row no longer prints its first line twice: the folded row still names a line of its reasoning next to 思考过程, and once you open it that preview steps aside so only the full text below shows the sentence (#1636).
- A room's own pending approval card is no longer announced as "Waiting for the owner's approval": the dock also reads the room's own
pendingApprovals— the array itspendingCountcounts — so the one 5 s poll where its own lists are behind stays quiet instead of hiding your own Approve / Reject buttons, while a card it never draws (decided on its reply bubble or in the Bot's chat) keeps the waiting line (#1638). - The "Waiting for the owner's approval" card in a group chat now says where the room's approval is decided: in a Bot's chat, with one button that opens it, or on the reply bubble above it, which is already on screen. When the dock cannot place the card it says only what it said before (#1640).
- The folded thinking row's one-line preview now skips fences and horizontal rules, cuts on a character boundary instead of halving an emoji, and ends a clipped line with an ellipsis; reasoning that is nothing but markup says how many lines it has instead of showing a fence fragment (#1642).
- A tool result now closes its foreground wait, so the 60-second steering line only aborts a turn that is genuinely waiting: a human line that arrives after a turn has merely been running for a while, with every tool already returned, no longer interrupts it. The Bot's "running tool" chip also clears as soon as the tool returns. ([#1644](https://g...
FlyBot v0.2.13
Changed
- FlyBot now builds, tests and installs on DeepSeek Harness
0.2.1-rc.2, the current npmlatest; the README commands, the CI workflows, the container image and the install help all pin that version (#1384). The container now runs with host networking and the web server binds the host's127.0.0.1itself, because DSH 0.2.1 refuses a wildcard host — same loopback-only exposure as before, Linux Docker only.
Fixed
- The "Hand a task to a Workflow" dialog now selects the first card of the Tasks board as soon as the board arrives, so
Assignposts the task instead of closing on a silent no-op, and an empty board leaves it disabled. When the Tasks board cannot be loaded the dialog shows the error with aRetrycontrol instead of claiming there are no tasks to assign, andAssignstays disabled while the load is in flight or failed. (#1457, #1458) - Workflow: unloading the plugin now releases the run Bots of every live run in a workflow, not only the last one, so a workflow with two concurrent runs no longer leaves hidden Bots that no saved record names (#1472).
- Fixed workflow Stop and Reset writing the state change before the run's Bots were released, which erased the only id record of a Bot whose release failed. A failed release now keeps that id named on the still-live run, so the next Stop, Reset, dispose or reload retries it. The run is left Paused with
releasePending, so it hands off nothing more andresumeis refused; Stop again is the retry, and a whole-workflow Reset freezes every run it did not reset. (#1476) - The Workflow settings page now reports a failed host call instead of staying on "Reading workflows…": a workflow read, graph save or model change whose host call throws, rejects or never answers ends on the page's own alert, a failed read offers "Try again", and a failed save no longer claims changes are saved automatically (#1504).
- The Workflow stage chat scrolls itself only while it is already at the bottom. A workflow node's own reply is filtered out of the stage, so it no longer drags the chat down, and a reader who scrolled up keeps their place. (#1518)
- A
workflow/resetwith no task to keep is now refused before it clears the lane, so a workflow that is waiting with queued tasks and no live run keeps its queue instead of losing every queued task to a refused reset, and a run whose Bot instances were never given a task no longer disappears with them (#1531). - The failure recovery edge added by #1529 reset only the recovery target and the failing node, so a node on the way back stayed
doneand the retry was fed the output it had just rejected. Recovery now resets every node from the target through the failing node and re-runs them in order. (#1541) - The failure recovery added by #1529 accepted a
maxfrom 1 to 8 but never recovered onmax: 1: the first failure already read as the limit, so the run failed at once and the attempt counter stayed empty.maxnow counts recoveries, somax: 1recovers once andmax: 8still means at most eight. (#1542) - A step that only mentioned
needs-humanoralready-claimedin its reply ended its own run, so quoting an issue title, writingNo needs-human label on this issue, or reporting the label as absent stopped the pipeline. A run now ends early only on a structured signal: a declared output equal to the marker, or a reply line that is exactlyEARLY-END needs-human/EARLY-END already-claimed. (#1546) - Deleting a running node from the workflow canvas no longer strands its run: the edit ends that run the way Stop does — the node is failed, the run is archived
failed, its node Bots are released and a queued task takes the lane. Before, the run stayedrunningwith a queued-only node map and no timer, so undermaxConcurrent: 1nothing could ever start behind it. (#1549) - Opening a face-to-face room for the first time no longer fails its history with
Maximum call stack size exceeded. The header effects now coalesce a synchronous session notification instead of re-entering themselves, so the room's transcript renders on the first open (#979).
FlyBot v0.2.12
Added
- A scheduled task can now be copied to another Bot or group chat: Copy on the task's row asks for the target and creates the same title, instruction and timing there, keeping the task's next run instead of re-anchoring it (#1287).
- The workflow engine has its running layer's pure half: given a job card, the definition and the facts just read,
workflow-run.jssays which node cards may open and, for every node that may not, repeats the definition's own refusal sentence — a card is queued for the node's role rather than mentioned, exists once per job and node across a restart, opens nothing while the job is held, and the job is done when its terminal node's outputs are current (#1272, step 4; #1316). - A Workflow is a group-shaped chat of local Bots. The sidebar opens the run, the flowchart can be dragged into a new graph, and a board task can start a saved pipeline such as 发布交付流水线. A Bot starts one with
run_workflowor defines one withdefine_workflow. An open task whose title, tags or assignee names a Workflow or a local Bot is handed to that runner; a task that matches nothing stays for a manual pick (#1318). - A Workflow run now makes one temporary Bot per node from that node's prompt, role, model and tools. Those Bots do the step and are removed when the run finishes, fails, or is cancelled, and they stay out of the sidebar. The prompt and the flowchart can be edited in Settings → Workflow graphs, on the flowchart, or with
define_workflow. (#1373) - Settings → Workflows lists user-owned Workflows with rename and delete controls;
workflow/renameandworkflow/deleteRPC persist changes and clean up runs (#1434). Factory presets remain read-only. - Settings → Workflow graphs is now canvas-first with a per-node inspector panel. Select a node to edit its system prompt, role, and tools — no more wall of textareas. Workflow stage chat reuses the normal FlyBot conversation UI (same message bubbles and spacing as Bot/group chats), replacing the previous pipeline-log card style. Sidebar Workflows list, Hand-a-task modal, and flowchart drawer now show visual mini-flowcharts instead of text chains like "Planner → Writer → Coder", with a unified visual language across all surfaces. Hand-a-task modal overflow fixed: content scrolls, Cancel/Confirm footer is sticky and always visible. Flowchart drawer defaults to showing the graph collapsed; system prompts expand only when a node is selected. Workflow runs now advance only along graph edges when ephemeral Bots finish — no timer-based demo-step bypass for live runs. The
workflow/demo-stependpoint remains for replaying canned demos only and refuses while a live run exists. - Workflow graphs now support choosing a model for the whole Workflow. Settings → Workflow graphs and the Details panel show the chosen model; when set, every ephemeral Bot in a run uses it instead of the seated Bot's model.
- Workflow failure recovery: A node can now recover from failure by returning to an upstream node and retrying, instead of stopping the run. Define a failure recovery edge with
onFail: truepointing from the failing node back to the recovery target, with a configurablemaxattempts bound (1–8, default 3). Example:{ from: 'review', to: 'draft', onFail: true, max: 3 }— when Review fails, the workflow resets to Draft and runs through Review again, up to 3 times total. After attempts are exhausted, the run fails cleanly. Does not interfere with existingfailOnceretry or success-pathloopedges.
Changed
- A Workflow run now waits for each node's Bot to reply before the next node starts, and that reply is the chat line. A second task queues instead of replacing the run that is already going. Replay demo stays a separate script and does not move a live run; after a live run finishes, the stage keeps that run until Replay steps the script, and from then on the graph follows the demo rather than the finished run. Each node can name its inputs and outputs, and a required input with no upstream provider refuses the start. A dispatch that does not land stays unhanded, is named in the chat, and is sent again on the next hand-off. An older saved demo still replays, and a task waiting on its own Workflow's
maxConcurrentcap starts when a run of that Workflow finishes, ahead of a newer assign. A 429 mentioned in a successful reply does not fail the step. A loop edge is the only cycle: it stops at max (1–8, default 3) or when its until output matches, and a when edge chooses a branch while the other is skipped. The next pass of a loop can take the other branch. Each node is an ephemeral Bot for that run, removed when the run ends, and it has the same file and shell rights as a normal Bot, while a connector is available only when its node'stoolsnames it andspawnpasses that list through. (#1402) - Workflow concurrency is now configurable per Workflow in Settings → Workflow graphs. The host-wide 6-run cap is removed; each Workflow's own
maxConcurrentlimit (1–8, default 2) is the only gate. #1525 - Workflow Settings says the run's full access is a normal Bot's instead of a per-Bot permission choice, and the delivery template lists only connectors a node really calls (
github). The README says plainly that namingbashgrants nothing.
Removed
- The repository metrics panel is gone (#1363). Opening Tasks shows that Bot's task board: there is no Today / 7-day GitHub block, no Retry or Diagnose, and no "from the package" repository header. The per-Bot task board and Workflow stay.
- The leftover delivery-board helper is gone (#1376).
issue-ref.jsexports only the matcher the dispatch and the task store call (issueRefs,namesIssue,cardNamesIssue):issueCardForTaskand the private half it alone used (cardMentions,mentionOf,MENTION,asNumber) were deleted with the board that read them, andcore-host.mdno longer lists them. - Workflow stage no longer offers Replay demo, Run demo, or stepped canned playback (#1443). Assign and live runs still spawn ephemeral Bots per node;
workflow/demo-stepremains for tests only.
Fixed
- In a group with a task board, the admin's
@Name #1234 …work dispatch is a board assignment now, not a round: the host writes (or updates) that member's card and wakes it in its own chat, and the room opens no group part for the line. A question, a remark, an@all, a line from a member who does not divide the board, and a face-to-face room keep their group turn. A card that is parked (result-readyorblocked) is restarted toworkingso the wake actually reaches the member, a card already blocked on an input is reused instead of duplicated, and a dispatch whose card write fails falls back to the member's group turn instead of dropping the line. (#1254) - Copying a scheduled task, and editing one, now reads only a task of your own user: a copy from another member's Bot or from a shared room's task is refused instead of carrying that task's title and prompt into a new one, and the same holds for Edit, whose source the room-membership rule used to let a seated member through. The refusal is the existing
No such tasksentence, so a peer's task reads exactly like an unknown one. A deployment with nobody signed in keeps the single-human behavior it had. (#1321) - A group work dispatch now reuses a member's card only when that card names the issue itself.
titleandtargetare read first, and a card'snotecounts only when neither of them names any issue — so a card for#999whose note says "blocked on #1234 first" is no longer rewritten and restarted for#1234, and the dispatch opens#1234its own card. One issue still gets one card: a card that names the issue only in its note is still reused. (#1331) - The workflow planner no longer reads a blank hold as a hold. A hold now names its holder or it is no hold at all:
''— the shape a pull-request query that answered nothing arrives in — and' 'plan exactly like no hold, and a hold no name can be read from is treated as no hold instead of refusing every node of a job with either no sentence or nobody's name. A real holder is unchanged: every card-less node is still refused, with that name and the definition's own hold sentence. (#1337) - The Bot settings drawer opens wide enough for its own controls: at 320px the "Name" field's label broke into "Nam" and "e", the "Test connection" button into "Test connecti" and "on", and Main Bot's "Change" into "Ch", "an", "ge". The Details panel now opens at 380px, a row's label stays on one line, a button never breaks its own words, and below 300px — the panel's floor in a narrow window — the row stacks so nothing is clipped to fit a width that is not there. A preview in the sidebar shows the words of a line and never its untrusted fence or the fragment an older write left behind (
from="User"> …). A Bot chat with nothing said yet, which the shell draws with no header at all, now names itself above the composer and says it is empty instead of being a blank column with a composer. The details drawer's copy button is named for what it does ("Copy conversation", not "Share conversation") and says "Conversation copied as Markdown" when the copy lands, or reports why it ...
FlyBot v0.2.11
Added
- Settings has a Workflows page: the factory presets, your own definitions as
workflows/<id>.jsonin the workspace, and which one this project uses. A definition is edited as a node table — id, name, role, inputs, outputs, guards, refusal — with the flowchart drawn above it from the same table and each guard showing the sentence it would print right now, or saying the fact could not be read. Customize copies a preset into a new file and never edits the shipped one, and saving an unedited definition writes identical bytes (#1272, step 3).
Changed
- A finished task card leaves the task board's active list, and the Done column shows the newest 20 archived cards; a group's overall card now completes when its last child finishes, is cancelled by a rejected approval, or fails because its Bot was deleted, and a Bot's board asks for that Bot's own finished cards (#1252).
- Feishu (Lark) is now connected on Settings → Connectors (左下角「连接应用」), not through the Secrets page or a config file. The one Feishu row offers two modes — a custom-bot webhook (outbound only, as before) or a self-built app with its long connection — and renders one field per credential: the App ID is kept in the record because it is not a secret, the App Secret only in
secrets.json. App mode proves the pair with one tenant token, opens the socket from the row, answers a message back into the same Feishu chat as the app, and offersChoose Bots; the row also carries 允许的飞书用户 — nobody by default, the sender who was turned away listed with Allow, and a field to paste anopen_id— so a stranger's message never spends the owner's tokens.Disconnectcloses the socket and clears both credentials. A replayedevent_idstill lands once, and a half-saved pair dials nothing and says so. The app pair is no longer read fromFEISHU_APP_IDon the Secrets page, andfeishuis now a reserved capability name. (#1266, #1292)
Fixed
- Claiming or assigning a card in a group now posts the one public line the room notice asks for —
接了 #1253,第一步是 …— in the claiming member's voice. The line goes into the room log without opening a round or waking anyone, takes the card's own note as the first step, and is not repeated for the same card within 30 minutes; progress stays on the card. (#1253)
FlyBot v0.2.10
Added
- A group's board can now be a pull queue: the lead enqueues an item for a role (
track_taskswithqueueandrole, no assignee), an idle member of that role claims it withclaimand one item is handed to exactly one member, and a member that cannot finish returns it with a one-line reason and aretryAt; the room board shows one line per role with its depth and oldest wait (#1247). - The delivery board now reads the repository this instance actually works in, and the header says which one it read and where the name came from:
delivery.repoin the instance settings first, then the workspace's gitorigin, then the package's own repository as a labelled default (#1250). An instance with none of the three refuses withno repository — set delivery.repoinstead of showing another repository's numbers. - Feishu (Lark) connector, phase 1: paste a group's custom-bot webhook URL on Settings → Connectors and this instance can post a text message into that group. The row is a sender — no Bot tools, no per-Bot switch — it proves the webhook with one test message when you connect, shows the reason on the row when Feishu refuses, and sends on demand with the existing retry policy (a rate limit is retried, a refused signature is not). The URL is kept as a secret and never appears in
state.json, the view or a log line. (#1263) - The Delivery board and the task board now draw their cards through one component, and a card says which side it came from: a repository card is keyed
owner/name#number— one key for an issue and the pull request that closes it — and a task cardtask:<id>(#1265, first step). - The Delivery board can read more than one repository:
delivery.reposnames a list (the olderdelivery.repostill works as a list of one), every name resolves with its own source, and the page draws one block per repository with its own state. A repository that cannot be read leaves the others on a partial board instead of emptying it (#1265, step 4). - Feishu (Lark) connector, phase 2: a message the owner types in Feishu can start a turn here over the long connection. The app pair
FEISHU_APP_ID/FEISHU_APP_SECRETis saved throughsecret-setand stays out ofstate.jsonand the view; the connection is off untilfeishu.enabledis true, dials only from an app pair that is complete, and only the owner's messages start a turn. A replayedevent_idlands once — the id is remembered only after the message reached its chat, so a handover that failed is retried rather than dropped — the message arrives through the same chat seam a friend's Bot uses, and the answer leaves again on the sameFEISHU_WEBHOOKthe connector already posts with, so one chat has one way in and one way out. (#1266)
Changed
- A group turn now shows who is working: one line per member that holds an open card on that room's board, with the card number, column, progress and how long it has been open, and an explicit
idle (no open card)line for a member that holds none — read from the board at the turn, with nothing new written and no one having to post a status (#1213). - A Bot condenses its conversation earlier: the steward's check line is now 45% of the model's usable window instead of 80%. Every call re-sends the fixed block and the conversation is most of each prompt, so condensing sooner cuts the prompt of a long turn by about a quarter; the cost is that a checkpoint arrives sooner and a condensed conversation keeps less detail.
checkRatiostill overrides it in the profile (#1233). - A round-trip now exists to bring back a fact the room did not have: independent reads go in one call, a fact already read is quoted with its anchor instead of fetched again, and the same message carries the tip sha, the CI state and the files read, because every avoidable round-trip re-sends the whole context (#796, #1235).
- A group chat now carries neither the composio tools nor the four
schedule_*tools: eight tool schemas, about 20.4k characters re-sent on every call of every group turn, that a room round does not use. A Bot reaches an outside app and manages its schedules in its own chat, and the refusal says so. The github tools and every built-in tool stay in the group, and a Bot's own chat is unchanged (#1237). - A connector call refused in a group room now says the tool is not available there, instead of pointing the caller at its own chat — a friend's Bot has no composio tools in its chat either (#1271).
Fixed
- The delivery header prints its "as of" time once while today is still running: the partial-day hint now follows the single time instead of repeating it from a second clock rendering, in both English and Chinese. A day whose runs the host could only count by their total now shows the same red share in the header row as the point drawn on the line beside it, instead of a dash next to a dot (#1244).
- A part handoff keeps a repeating task exactly where it was: the same id, the same delivery history and the same instant it was already due, instead of re-anchoring the next run to the handoff and hiding the move in a reset history. (#1245)
- The delivery board reads
delivery.repohowever the instance wrote it — nested (delivery: { repo: … }), as a literal dotted key, or flat (deliveryRepo) — instead of only the flat one, so the setting the page and the hint name really names the repository (#1258). A workspace that is a linkedgit worktreenow resolves itsoriginthrough the worktree'scommondirrather than falling back to the package default, and a repository name with a.or..segment is refused instead of being turned into/repos/../../issues. - A line sent into a group chat while a member is speaking now reaches that member inside its turn: the round watches the same Session queue it drains, logs the line at once and steers it into the speaker's running turn with a header saying it arrived after the turn started, so the member's next step answers it instead of reading it after a long turn as if it had been there all along. The line leaves the relay queue, so it is neither logged nor answered twice. The one exception is an explicit stop (停, 停下, 停止, 别做了, 不要做了, stop, stop it): the speaking slot is marked stopped by the user, its turn is cancelled and its answer is not posted, and the line stays queued to be answered as a fresh line by the room (#1267).
- A Bot whose tool list has to be rebuilt for a group room no longer ends up with every tool listed when one name in the mask cannot be restricted: the host keeps the narrowing it can enforce and warns, instead of dropping the whole mask after lifting it (#1271).
- A turn no longer stays open because making room before its next step never returns: the pre-step compaction now carries a deadline (
COMPACT_DEADLINE_MS,config.compactDeadlineMs, five minutes) beside the turn's own signal, and a summary request still retrying when it passes ends that turn with a warning instead of parking it —turn/endruns, the queued messages stay for the next turn, andstop_botis no longer the only way out (#1285).