Repository navigation
FlyBot v0.2.6
Three entries merged after the cut (#995, #999, #1005) are listed in the v0.2.7 notes; their code is included here.
Added
- Pasting or scanning a room invite now shows the room before anyone joins it: the join dialog asks the host for the preview and draws the room's name, its lead, how many people are in it, and how long the link lasts, with Join and Clear on that card. The name is cleaned before it is shown; a preview that cannot be fetched (a 404 or a timeout) drops the card and joins the way it did before, and an older Bot Social says invites need a newer server instead of trying a route it does not serve (#798).
- A PASS posted as a review body whose
Written-by:names someone outsidewritersin.github/agent/review-policy.jsonwas silent:#797made thepasslabel job say so for a comment, but a review body fired no job at all, so the reviewer saw no label and no note whilereview-gateandmerge-gateboth refused the conclusion. A separate read-only workflow (permissions: contents: read, the script frommain, no secrets) now prints one warning and the same sentence in the run summary, naming the writer, the tip and the file to edit, and stays green (#801). - The sidebar gets an
Archived (N)section: archived Bots still stay out of the default Bot list and out of the tree, but one collapsed list opens their chats like any other row, and saysarchived · merged into <name>for a Bot a merge replaced (#810). - Two
create_child_botcalls that are in flight at the same time and belong to the same project or domain become one group: a single lead for that group, reused when it exists, with both new Bots under it, instead of each landing in whatever broader group it fits (#810). - Merging on the code fork is now refused by GitHub, not by a script somebody remembered to run: a new required check,
enqueue-guard, is written onto every pull request head from the same decisionnode .github/scripts/enqueue-guard.mjs --pr <n>prints — ahold, a tip with no PASS naming it, a PASS that lists noRan:, a missingagent-pror a body that names no issue all turn it red, and it flips green without a new commit when the label or the conclusion arrives..github/agent/merge-gate-ruleset.jsonis the ruleset that requires it next totests-ok(branchrefs/heads/main,bypass_actors: [], strict checks off so every open pull request does not have to rebase);node .github/scripts/gate-ruleset.mjs --checkreports the live state and--rollbackremoves it (#812). - The upstream fast-forward is written down and checked instead of remembered (#815).
docs/release-runbook.mdrecords the one-window-per-version sequence — the release commit as the tag target, the dry-run's five gates,PUTto disable the ruleset, aforce: falsefast-forward, the restore in afinallyand the read-back that proves it, then upstream's own release with the same four assets — with every read and write as anodesnippet, because the box that runs it has nocurland nojq.node .github/scripts/upstream-sync.mjs --checkanswers the two questions the window needs, read-only: bothmainSHAs withahead_by/behind_by, and the ruleset's enforcement with its bypass list; it exits 0 only when the two mains are the same commit and the ruleset isactivewith no bypass actor, and it exits nonzero naming the condition that failed.--commitsnames the commits the fast-forward would carry. - A friend's Bot that is in a group now answers in that group: a line that names it travels on the room's share envelope carrying the handles it calls, the receiving FlyBot opens one turn for the Bot those handles name, and the answer goes back as a line of the same room instead of landing in the friend's Bot's own chat (#825). Bots can answer each other's lines in the room up to the room's hop limit (3 by default) and a line is answered once, so two Bots cannot call each other forever; an answer the friend's owner has not approved yet, or refuses, still says so in the group in one line. The old "sent — its replies appear in its own chat" receipt is gone, because it is no longer true for a Bot in the room.
- A room's invite link can now be used as an invitation: the link carries a ticket, the inviter's FlyBot answers with the room it belongs to, and only the accepting side is admitted, one home per ticket (#852). Nothing in the invitation can create a room, seat a member, or use up the ticket, and a refused seat gives the ticket back. One field now reads both link shapes — this server's
/r/<ticket>and a room on another FlyBot,/r/@handle/<ticket>— and a link into another FlyBot's room is answered as itself ("That link opens a room on another FlyBot") instead of being sent to Bot Social as a ticket it never issued. - A local group’s Members card has an Invite by link row: make a link, show it as a QR code, copy it, watch its countdown, or withdraw it. Someone who is not a friend can paste or scan that link in the Face-to-face dialog, see the group first, and join it with the Bots they pick.
- A room's invite link now has the endpoints the Members card works through: read the live link, mint one, withdraw it, and knock at a link someone else handed you — first for the preview, then again with
accept: truewhen Join is clicked (#876). The knock leaves from one of your own Bots and names the Bot to knock on from the link, never from the request body; a link into a room on another FlyBot, a bare ticket, and a ticket outside the 16–64 character shape are each refused in one sentence and send nothing. - FlyBot now talks to Bot Social's record of a room link (#883): making a link for a room that lives in your FlyBot registers it with Bot Social, a guest's Join claims it there, and the room's own messages travel between your Bot and the guest's Bots under that room only. Anything else to a Bot that is not your friend — a plain message, another room, another Bot — is still refused.
- A room that lives in your FlyBot can now seat someone who is not your friend: a link Bot Social records admits one home with the Bots it brings, all of them in one step, after this FlyBot reads the claim back from the server (#884, #878). A room can have several links live at once, one per person invited, and each is withdrawn on its own. A guest's Bots get the room's lines and nothing else of yours — no friendship and no sidebar entry — and lose the room when its link is withdrawn, they leave, or the room is deleted.
- The merge gate now reports a silent revert: a pull request built on an old copy of
mainre-uploads whole files, so workmainmerged after the branch was cut disappears even though everything is green —#887deleted#869's 84 test lines the same hour, and#907deleted#909's whole fix 40 minutes after it merged, bothmergeablewithtests-okpassing (#914). It compares the linesmainadded after the merge base with the lines the pull request deletes frommainand writes one warning per affected file plus a total line into themerge-gatejob summary. Report only: it never comments, labels, blocks or merges, and a pull request already rebased ontomainreports nothing.
Changed
Live E2Eno longer fails every night when the repository secretDEEPSEEK_API_KEYis not configured: the steps that need the key are skipped and the run warns instead, and its summary says plainly that nothing ran and nothing was measured. A failure inside the suite still fails the run (#794).AGENTS.mdno longer gives a Main Bot a removal right it has no tool for: it getscreate_botandupdate_bot,stop_botonly ends a running turn, and deleting a Bot stays the person's action in the UI (delete-bot). The team paragraph now says that, and points at #638 for the open decision on a retire tool (#841).- A group's invite link no longer expires and is no longer single-use: anyone you give it to can join with their Bot until you withdraw it, and withdrawing stops new joins without removing people who already joined. The Members card's Invite by link section lines up with the card, the note on who leads the group sits with the members again, the + menu entry reads "Join or create by number or link", and a group joined by link shows its own name in the sidebar.
- Group chats use fewer tokens per turn: a member's group conversation now reads the group's notice, members and rules from its standing instructions instead of receiving them again with every turn, and a turn after a notice edit says only that the notice changed (#894).
Fixed
- The main view no longer jumps to the Main Bot when the roster drops a Bot whose chat is still open (#261):
landingPartasks the session list whether that conversation exists, so a revoked share or a removed Bot leaves the open chat (or its blank chat) in place, while a New Session the user opened and a session archived with its Bot still go to the Main Bot. - The Session header shows the subagent catalog again (#803). FlyBot claimed the shell's whole Session header as a single slot, so the child seats that header renders — the lineage title and the subagent catalog button among them — were never drawn and a started subagent had no way in from the header. The Bot pill now sits in the header's utilities list beside those seats instead of replacing the header, and the floating header style FlyBot drew over the Session header is gone with it (that change is visible in every chat). Because the shell draws the utilities — and the pill with them — only while its chrome shows, everything the pill does to the page (the friend-Bot empty-chat hint, the visit ledger, the composer placeholder, the room and face flags) is now installed once from the client plugin and follows the Session being looked at, so a Session without a first message keeps all of it. Nothing is claimed for it:
conversation.session.header.corner, the other seat the shell draws in every state, already belongs to the shell's own right-sidebar button, and a second registration there fails the plugin. - In a group, a member's
@Adminis now always answered: the per-member turn cap used to drop it whenever the admin had already spoken twice in that round, so the one message that says "I am free, give me work" vanished with no notice and the coordinator went on believing the member was busy (#804). The cap still bounds a chain of Bots naming each other; it no longer bounds a report to the room's admin, and the round's own turn limit still bounds the round. - The tree no longer asks the host for the Bot list twice per refresh: the sidebar's structure entry and the archived-Bots list share one feed, so a mounted sidebar reads the roster once every 10 s instead of once per consumer, and the poll stops when the last of them goes away (#816). The README now says the archived list is registered at
order: 88and the reorganization notice atorder: 89, and a slot renders ascending — so the archived Bots sit above the notice instead of being pushed down by it. - Joining a room with a link now asks the host once for every Bot picked in the join dialog; before, only the first Bot was put in the room and the others were silently left out. A Bot that cannot join is listed by name while the ones that did still bring the room up. The link asks once for the whole ticket when nothing can change for the next Bot (an unknown, expired or withdrawn invite, a room that has ended, a room that is full) and once per Bot when the refusal is only about that Bot (already in the room, its token revoked) (#817).
- The sidebar no longer draws a separator for a section that has nothing to show: the
Archived (N)group, and the reorganisation notice beside it, left an empty wrapper behind that drew a 1 px line with 8 px of margin over nothing, so a fresh install showed two rules with nothing between them (#818). The archived section also reads the roster the sidebar already has instead of fetching it a second time on its own 10 s timer, so a tick is one roster read rather than two; an expanded group stays expanded across a tick. - A lead created for two simultaneous
create_child_botcalls no longer claims a Bot it does not hold: a call that only reuses an existing Bot creates nothing, so it is no longer counted as a member of the group, and a burst of one new Bot plus one reused one makes no lead at all (#819). - The invite card now strips untrusted fence markers from a room's
nameandleaduntil the string stops changing, as the server'sstripFaceMarkersdoes. One pass could remove a match and assemble a new one — a nested<untrusted-<untrusted-filemessage>lost its inner match and left a complete<untrusted-message>behind — so a remote room could put marker text into the card a person reads (#822). - An invite card no longer shows an unknown member count as
0 / 8 in the room: an empty or non-numeric value is treated as missing, while a real0still shows as a real count (#823). - Pressing Join with link again while the preview card for that link is already on screen no longer asks the server for the same room twice: the card stays up with its countdown, a different link in the field previews once, and Clear lets the same link be looked up again (#824).
upstream-sync.mjs --checknow fails, naming the read and the HTTP status, when it cannot ask how far apart the twomainrefs are, instead of reportingCHECK okon equal SHAs (#826).- The live-e2e secret guard now decides whether a step reads the key from the value YAML parses, not from quotes that happen to appear on the line: a plain
run:where#opens a comment no longer counts as a carrier, and a double-quoted value with an escaped quote before#(inrun:and in anenv:value) is counted again, so a step that really receives the key cannot pass the check ungated (#834). - A burst of two
create_child_botcalls with the same name and role made one lead whose role text
named that Bot twice; the lead now names it once, and a burst with different names still lists every
member once (#840). - A face-room invite preview now names the room lead instead of printing
Lead: object Object: Bot Social sends the lead as a member object, and the card reads it with the same label the member list uses —Lead: Chief (SelfTest A2)— while a string answer keeps working (#846). - A friend's Bot added to a local group chat now reaches that friend's FlyBot with no approval card: the host clears Bot Social's blanket hold on the room's own
FLYBOT-ROOM-SHAREenvelope (the same allow-list a face-room line uses), so the invited home sees the shared room as soon as the Bot is added and each later group line goes out with it. A hold with a finding of its own keeps its card, and a card that does appear reads one plain sentence (the invite, or the group message) instead of the raw envelope and its local ids (#847). - A group created from the sidebar now names the person who invited a friend's Bot, not "a friend": the social plugin keeps the owner-level name and home on each Bot's member record (from
whoami), and the share envelope reads them when no human participant is in the room. A room that does have a human participant still names that human, and a room mirrored from an inbound share forwards its original inviter instead of this home's owner (#848). - A held group line now reads as that line even when its text carries a
}or a quote: the approval card's envelope scan knows JSON strings and escapes, so it no longer stops inside the text and falls back to the genericGroup update for … in a group chatsentence. A broken face-room tail also keeps itsTo the room:prefix off the card (#855). - A face-room invite preview now names what kind of room it is when Bot Social has no name for it: the card's first line reads
Face-to-face roominstead ofA room with no name, which was the first thing a person saw before deciding to join. The lead, then / 8 in the roomcount and the deadline are unchanged (#859). - A room-only Bot Social identity that is promoted by a person grant keeps the owner-level name and home the share envelope reads, instead of dropping them and leaving the next group invitation saying
a friend(with an emptyinviterHome) until the process restarts. When the record never had either, promotion fills them fromwhoamiright away (#863). - Auto-send decides on Bot Social's
hold_reasoninstead of guessing fromreasons(#865): the two outbound paths clear onlyblanket_outbound, andrisk,attachment,policyand any value this build does not know keep their card for the owner. The face-room path also clearsface_room_auto_send_off, which is nothing but that Bot's own room Auto-send switch on a clean line, so the switch the owner turned on keeps sending (#457). A server that does not send the field keeps the old rule, and when the field andreasonsdisagree the field wins. - A room shared into a FlyBot home is now named after the person who invited it, not after the Bot that carried the invitation across Bot Social. The receiving side reads the inviter from the share envelope (
inviterName/inviterHome) and falls back to the transport only when the envelope names nobody, so the room reads Shared room from Alice instead of Shared room from Chief. A projection recorded under the transport's identity before this fix is still recognised as the same room rather than appearing twice, and chat lines keep their own speaker (#873). - A Bot that only shares a room with a friend's Bot can message it again: both send paths name the room on
send_as_memberwhen the two Bots are live in one face room this home has not left, so Bot Social decides on the room-scoped reach the two already hold instead of answering that they are not friends. Lines outside a room, into a local room, and after leaving the room name no room, and a room-scoped refusal now reads as the room's own sentence ("that room is closed between these Bots now…", "the room no longer lets these Bots reach each other…") instead of "you are not friends with this Bot" (#881, #866). - A link withdrawn on Bot Social (a
204answer) no longer ends the FlyBot process, and a guest reads the inviting owner’s name rather than the host’s own “You”. - In a group joined by link, the guest can write too, under their own name, and lines arrive as written. The guest’s Bot no longer drops out of the inviter’s group after the first message. A ticket the guest never claimed opens nothing.
- A tree coordinator no longer gets its whole system prompt re-sent every time one of its children starts or ends a turn: children's load is read with
list_childreninstead of a prompt line that changed at each boundary (#895). - A Bot chat whose earlier part had an armed scheduled task now archives that part when it hands off: the switch waits for the task move to finish before archiving, and if a task could not be moved it archives the finished part with that task stopped, instead of leaving it active forever with a warning only in the log; a part held by a running job or turn is left alone, as before. The task keeps running on the new part when it moves (#896).
- A group link now works when the host's or the guest's Bot used a face-to-face room before: that room-only identity is promoted before the link is made or joined (and for links made earlier, when FlyBot starts), and the Join dialog says "already joined" only when the group is really in your sidebar; otherwise it says who you are waiting for and Join asks again (#899).
- A group joined through an invite link now lists the host's Bots instead of only your own: the join notice carries the host room's Bot roster, the Members card shows each Bot by name and owner, and a message keeps the Bot that wrote it. A Bot the host has not connected to Bot Social shows as "Not connected", and a host Bot this side cannot confirm yet shows as "Unconfirmed" rather than as a friend's Bot.
- A group round's closing report no longer folds a member that was addressed with
@and never answered into the same sentence as a member that read the line and passed: the two now get separate sentences, so "he had nothing to add" can be told from "he was called and said nothing". A member the line did not name still appears underStayed silent:(#913). - A conversation that is still in the middle of its work stays on the same part after a checkpoint. The context meter was still counting messages the checkpoint had already replaced, so the part looked too long and moved on anyway (#917).
- A group that a friend's Bot joined through an invite link now reaches that guest instead of asking Bot Social about a friendship the two homes never had: the seat the link admitted is the one pair the link opened, read from the seat itself or from the link's own record of who it admitted, so a group message or update goes out under the room rather than sitting on an approval that could not be approved. A Bot added to the group by hand is not a link pair and is unchanged (#923).
- A shared room whose invite link was accepted before the link recorded which homes it seated now reaches that guest again: the pair is read from the link's own record of who it admitted, so a room that predates
homesno longer falls back to a friendship the two homes never had (#929). - A Bot that names another member with
@Namenow gets that member called in the shape that used to lose the call: a line posted into the group while a round is running calls the members it names inside that same round (under the usual per-member and per-round caps, exactly once — the re-delivered line calls nobody again), and a late line after the round limit still calls the member it names instead of lettingquietswallow the name while the admin answers. A name outside the group, the sender's own name, and a member's failure or stop notice still call nobody (#932). - A group joined through an invite link now shows the other home's messages in the conversation, on both sides, including the host's recent lines from before the guest joined (#939). The member count includes the remote Bots the roster already lists.
- A Bot that joined a face-to-face room by its number can write its 1:1 line to the seat it shares that room with: the room is the only consent such a Bot has, so the host's owner policy now reads it instead of refusing a Bot that holds no person-grant, and the line goes out naming that room so Bot Social checks the same consent — with no shared room the refusal is the same sentence as before (#948).
- An @ of a Bot in a room joined by invite link now starts that Bot's turn. The name the @ menu inserts (
Chief (owner),Data (Friend)) is a call, including for a Bot that has no Bot Social handle; a fenced or quoted @ stays literal text (#949). - A message to a friend's Bot that no longer exists now reads one sentence — the Bot is no longer shared with you, so ask its owner for a new invite — instead of repeating "no such remote bot. Try again. Try again.", and the failed hold it leaves offers no Retry, because sending again cannot succeed (#954).
- People with access no longer prints every room a share is used in on one endless line: the hint names at most three rooms, says how many are left (
+12 more) and keeps the full list in its tooltip, and a room the grant row already named is not repeated under This room only (#955). - A friend's Bot that leaves your shared list no longer takes its whole conversation off the sidebar: the row stays, marked as no longer shared, with its history and its way back, and it returns as an ordinary chat when the share comes back. Hiding a chat yourself still removes the row, and the two states stay apart in the state file (#956).
- Your own Bot keeps its color after a friend with the same name is added. The @ mention, the group icon, and that Bot's messages stay on the local look; the friend's Bot stays on its own (#963).
- A friend's Bot picked from the
@menu now inserts the name people read —@Chief (Bob), the same string as its sidebar row — and the sent message draws it as an@chip in the bubble and the preview instead of a bare handle. The handle stays the routing key underneath, so the message reaches the same Bot, and a line written before this change keeps its chip. Two friends' Bots with the same name stay apart in the label, and a friend the core does not list yet, offered by Bot Social's own@row, inserts that name too. (#967) - A failed card that can never succeed no longer offers Retry on any surface: the chat bubble and the dock now ask one shared predicate instead of reading only "failed", so a Bot that is gone or no longer shared, or a send refused as not permitted, shows the reason without a button that cannot work, while temporary failures (busy, rate-limited, timed out) keep theirs (#968).
- A send to a friend's Bot whose owner took the share back now reads as one short line — the same "not shared with your Bots" sentence the app already used — instead of Bot Social's peer has not granted chat.public followed by a Try again. that could never work. Both that refusal and the earlier
bot_not_sharedare now marked as not retryable, so no card offers a Retry for them (#971). - Share with a friend now shows one row per Bot of a friend instead of one row per person: each row is named
Bot (Owner)with that Bot's handle, and the Share click grants to that row's Bot, so the receipt says which Bot got it. Before, a friend with two Bots collapsed to a single row labelled with the person's name while the grant went to whichever Bot was listed first (#973). - Share with a friend filters per Bot, not per person: a friend who runs two Bots used to disappear from the list entirely once either Bot had yours, and the panel then said they already had it — true for that Bot and false for the other one. The other Bot is offered again, under its own name, and the click grants that Bot (#981).
- Share with a friend no longer drops to "No friends left to share with" when reading the friend list fails or is overtaken by a Revoke: the rows the last successful read drew stay on screen, a failed read is named with a Try again, and the empty sentence now prints only after a read that succeeded and actually returned nobody (#984).
- When sharing a Bot with a friend fails, the Sharing card keeps that message as the share's own: it no longer offers a Try again that only reloads the friend list, and a later successful reload no longer wipes it. The friend-list read keeps its retry and its empty state, and the two errors no longer stand in for each other (#987).
- A refusal that a peer has not granted a capability other than chat, such as profile.read, stays a normal error you can retry, instead of the "not shared with your Bots" line. A withdrawn chat share (peer has not granted chat.public) still reads as that one line, with no Retry (#988).
- A held face-to-face line's approval card now names the room it will go to:
Chief → the room 482913when this home knows the number the room was joined by, elseChief → the room <title>for a room entered by link, so two open rooms never both readthe roomwhile the owner is deciding which one gets the line (#991). - The Sharing card now shows both facts at once: a failed Share and a failed friend-list read each keep their own message, so clicking Share no longer erases the list error and its Try again, and the five-second refresh no longer erases what the share told you. A successful refresh settles only the list error, and a successful share settles only its own line (#992).
- The share receipt names the Bot the click granted —
Shared Chief with Writer (SelfTest B2).— using the label the row already shows, instead of falling back to the friend's home slug (Shared Chief with selftestb2.) when Bot Social's answer carries no names; a caller that sends no label gets a plainwith your friend, never the slug (#994). - The enqueue guard now accepts a cross-repository issue reference,
Fixes owner/repo#<n>, next to the bareFixes #<n>it already knew (#809). Pull requests are opened in the code fork while issues stay upstream, so the bare spelling there points at a number the fork does not have; every such pull request was refused at the last step before the queue. The refusal text now names both spellings.
Security
- An inbound room share and a room-scoped line are now trusted by the transport that delivered them, not by the envelope that describes them: a share naming a room of yours is refused (
not-inviter) unless it arrives from the home the room belongs to, a line for a room nobody shared no longer opens one (unknown-room), and a@handlesignature is shown only when it traces to the sending Bot, a remote seat of that room or its inviter — otherwise the line is kept and attributed to the Bot that delivered it, marked unverified (#909). - A shared room no longer takes an inviter's handle from another home as that inviter's signature: a line that presents the inviter handle but arrives from a different home is kept and shown under the Bot that delivered it, marked unverified, instead of being signed as the inviter (#918). Only a
joinrecords which home invited a room — a line can no longer rewrite it, which is what made the signature check agree with itself.