Repository navigation
docs: define team ownership and Chief collaboration boundaries - #59
Merged
Merged
Conversation
Fixes #46. Planned multi-human authority contract (human owner <-> designated Chief <-> specialists) written before schema work (#47+); team mode stays disabled until the documented authorization gates pass, and nothing here claims multi-human is shipped. Issue #16 stays open: both Option A (restrict create_child_bot to root/Main/Chief or require owner approval) and Option B (amend AGENTS.md so specialists create children within owner limits) are documented as unresolved; schema and tests must assume neither. Co-authored-by: hs liu <hsliuustc0106@users.noreply.github.com>
HugoXK
pushed a commit
to HugoXK/FlyBot
that referenced
this pull request
Oct 24, 2026
Owner: Dev3 Reviewer: Reviewer2 Written-by: Dev3 Fixes ThinkFlowLab#923 A seat a link admitted was only a pair when the seat record kept `admittedBy: 'ticket'` **and** `hostBotId`. The local link-accept path (`answerLocalInvite` -> `addRoomParticipant`) wrote neither, and a seat older than those fields has no way back — so `ticketPeers()` (`core/src/host/reach.js`, the list Bot Social's room reach is derived from) skipped the seat, the room's own envelope fell to the plain pair path, named no room (`plugins/social/src/social.js:1622`), and Bot Social held it as `pending_approval`. Approving that card then ends in `no active relationship between these bots`, which is exactly what the owner's screenshot shows (`Group update for Chief`, `Bot Social decide_approval: no active relationship between these bots`). The card is **outbound**: `shownRoomShareText` (`plugins/social/client.js:1211-1221`) renders every op other than `join`/`leave`/`line` as `Group update for <to>`, and the `Approve` / `Auto-send from now on` pair is the `ThinkFlowLab#417` "my Bot -> that person" hold. It proves this host cannot reach that guest; it is not evidence about the reverse direction (see "Open, not a gate"). ### Reproduce 1. Host room `war`, invite minted from the room (`mintRoomInvite`), a Bot of another home accepts (`op: 'ask', accept: true`). 2. `core/test/remote-room.test.js` (new test): the seat record has no `admittedBy` / `ticket` / `hostBotId`, and `ticketPeers(state)` has no pair for it. 3. A room-share envelope from a local Bot to that seat therefore reaches `sendOut` with `ticketPair()` undefined; `roomId` is `undefined` for `via: 'room-share'`, so `send_as_member` carries no room, Bot Social answers `pending_approval`, and `decide_approval` ends in `no active relationship` (bot-social `deliverMessage` -> `activeRelationship`). ### What changed - `core/src/host/reach.js`: `linkSeats(room)` reads the link's own `homes` (home -> the handles it seated, written on the accept), and a remote seat is a pair when it kept the ticket **or** a link of this room names it; the pair's `from` is the seat's `hostBotId` when it is a current member, else the room's lead (`room.admin`). A friend's Bot added by hand is named by no link and stays out. - `core/src/host/room-membership.js`: `addRoomParticipant` takes `opts.ticket` / `opts.hostBotId` and records `admittedBy: 'ticket'` / `ticket` / `hostBotId` on the seat; `answerLocalInvite` passes the link's ticket and the Bot the link was minted for (or the room's lead). New seats are right from the start; older rooms are covered by the reach-side record, so nothing is rewritten on load. - `plugins/social/src/social.js`: comment only. The old sentence said a local room "has no id on the server" — stale since ThinkFlowLab#59: `mintRoomInvite` passes `roomId` to the server (`room-membership.js:1279`) and `sendTicketShare` names it. What the ordinary path lacks is the pair, not the id. - `docs/subsystems/core-host.md`: one sentence for ThinkFlowLab#923 in the room-share section. - `changelog.d/923-room-link-seat-pair.fixed.md`. No change to the server, no widening of `activeRelationship`, and no new rejection: `not-inviter`, `unknown-room` and the `ThinkFlowLab#909`/`ThinkFlowLab#910` trust boundary are untouched. ### What I ran - `core`: full suite `node --import ./test/harness-resolve.mjs --test --test-concurrency=1 test/*.test.js` -> **1344/1344** - room suites (`remote-room` + `room-ticket-seat`) -> **75/75** - `plugins/social`: `node --import ./test/resolve.mjs --test "test/*.test.js"` -> **352/352** - `node scripts/ds-bot-patches.mjs generate` + `check` -> 10 patches apply cleanly; `core/` equals `ds-bot/` + patches; the three mirrors are byte-identical to their core files - `cd core && node scripts/lint.mjs` -> oxlint **0 errors** (44 pre-existing warnings), `arch-docs: OK` - `node .github/scripts/changelog.mjs check` -> 45 fragments well formed ### Red without the source change With only `core/src/host/reach.js` and `core/src/host/room-membership.js` reverted to pristine `0b22aa53` (the new test and the rest kept): `not ok 55 - ThinkFlowLab#923: a link-admitted seat is the pair the link opened...`, **64 pass / 1 fail**, failing at `seat.admittedBy === 'ticket'` (`undefined`). With the change: that file **65/65**. The social assertion added to the `ThinkFlowLab#883` test is a **guardrail**, not a red case: it pins that an approved (`approved: true`) room-share envelope still goes out by the pair and names the room, which is the "settle" half once the pair is enumerable (`sendOut` checks `ticketPair` before every other branch). ### Not run - Smoke test: this session has no DSH checkout for `dev/bench.sh`, so it was not run; the unit, plugin and arch-docs suites are the coverage here. - No live two-home run: the reproduction is in-process (`remote-room.test.js`), which is how `ThinkFlowLab#852`/`ThinkFlowLab#884` are covered too. ### Open, not a gate Whether the reverse direction (guest -> host) fails for the same reason is **not** decided by this PR: it needs the other machine's screen (`Not sent: ... you are not friends with this Bot` would mean the same server-side fallback at send time; `已发送` would mean delivery). The server half is `Birfy/bot-social#67`; this PR only makes the pair enumerable and leaves every existing rejection in place. ### Risk - Reach is widened only for seats a link of that room admitted; the negative control (a remote Bot added by hand) is asserted in the new test, and the `ThinkFlowLab#884`/`ThinkFlowLab#883` tests are unchanged and green. - A legacy room whose link record no longer names the seat (for example an invite pruned from `localInvites`) stays non-enumerable and keeps today's behaviour: the card, and the server refusal. - `hostBotId` falls back to the room's lead when the seat did not keep it; that is the Bot the link was minted for (`inviteBotOf`), and it must still be a current member. Docs-not-needed: the `plugins/social` change is one comment plus one assertion on the existing pair path; no plugin behaviour, route or setting changed, so `docs/subsystems/social.md` would only restate what the `ThinkFlowLab#883` test already pins. The room-share pair itself is documented in `docs/subsystems/core-host.md` in this branch. Self-check: `git diff <base>...HEAD -- plugins/social` is 3 comment lines and 5 test lines.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Doc-only Magpie follow-up to #45: define the planned multi-human ownership / authority contract before schema work (#47+).
docs/subsystems/team.md— identity (human owner ↔ designated Chief ↔ specialists), invariants, routing matrix with table examples (same-owner delegation, Chief-to-Chief, forbidden cross-owner specialist, team-admin vs private-owner, no-forwarding-to-approval), ownership/disabled-Chief lifecycle, team-mode gateFixes #46
Verification
opencode/mimo-v2.6-flash-free) implementnpm run arch-docsOK ·npm run lintexit 0 ·npm test580/580 (Node 22)ds-bot//patches//core/src//plugins// workflow editsOut of scope
Implementation, social federation, resolving #16, inventing APIs.