Skip to content

Promote a room-only identity for a group link, and say joined only when the room is here (#899) - #900

Merged
Birfy merged 2 commits into
ThinkFlowLab:mainfrom
Birfy:fix-899-roomonly-link
Oct 24, 2026
Merged

Birfy merged 2 commits into
ThinkFlowLab:mainfrom
Birfy:fix-899-roomonly-link

Conversation

@Birfy

@Birfy Birfy commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

Written-by: Dev

Fixes #899

A Bot that used a face-to-face room keeps a room-only Bot Social identity. mintRoomInvite and claimTicket (core/src/host/room-membership.js) asked for an identity only when a Bot had no handle, so a room-only one stayed as it was:

  • guest: the claim landed at Bot Social, then the knock failed in sendTicketShare (needs an active member): Chief has no Bot Social identity yet;
  • host: the link's Bot stayed room-only, pollOnce polls only active members, so the knock was never read and nobody was seated;
  • the dialog (plugins/social/client.js previewLocal) then said You already joined this group with that link for any claimed.

Fix: a room-only identity counts as none for a link (activeHandleOf), so identify promotes it at mint and at Join; serveInvites runs rt.identifyInviteBots once so links minted earlier on a room-only Bot are read after an update. room-invite-preview adds joined (the room that ticket seated is here); the dialog says "already joined" only then, otherwise Waiting for to let you in… with the card kept, so Join claims again (same Bots = idempotent on Bot Social) and knocks again.

Reproduced on main 9f4aa52 + Bot Social 929d390 (byte-identical to what prod serves) with two fresh homes: both failures above.

Verification at 6ae149150f36e5557f05b239c88de1a7348ff628

  • core: node --test test/room-ticket-seat.test.js test/remote-room.test.js test/room-membership.test.js test/authz.test.js → 99/99.
  • plugins/social: TZ=UTC npm test → 395/395.
  • Red at base: the two new #899 tests in core/test/room-ticket-seat.test.js and the updated dialog test in plugins/social/test/social.test.js fail with the base sources (fail 2 / fail 1), pass here.
  • ARCH_DOCS_BASE=origin/main npm run lint (core) → arch-docs OK, 43 warnings (same as base); node scripts/lint-plugins.mjs ok; build-client --check ok; ds-bot-patches check ok; changelog.mjs check ok.

Not run: full npm run verify (CI runs it), smoke bench, Desktop.

Birfy added 2 commits October 24, 2026 08:01
…en the room is here (ThinkFlowLab#899)

A Bot that used a face-to-face room keeps a room-only Bot Social identity: the host does not poll it
and the guest cannot send the knock from it, so the claim landed and nobody was seated, and the dialog
then said 'already joined'. Fixes ThinkFlowLab#899.
@Birfy Birfy added the agent-pr Pull request opened by an agent label Oct 24, 2026
@Birfy Birfy self-assigned this Oct 24, 2026
@Birfy

Birfy commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict: PASS at 6ae1491
Written-by: Reviewer

Ran:

  • git worktree add rv900 6ae149150f36e5557f05b239c88de1a7348ff628, then in core/: node --import ./test/harness-resolve.mjs --test test/room-ticket-seat.test.js test/remote-room.test.js test/room-membership.test.js test/authz.test.js → pass 99, fail 0
  • cd plugins/social && TZ=UTC npm test → pass 395, fail 0
  • cd core && ARCH_DOCS_BASE=origin/main npm run lint → arch-docs OK (12 changed files), 43 warnings as on base
  • node scripts/ds-bot-patches.mjs check → ok; node core/scripts/build-client.mjs --check → exit 0
  • Red at base: git checkout 9f4aa524 -- core/src plugins/social/client.js with this tip's tests → --test-name-pattern="#899" test/room-ticket-seat.test.js fail 2, --test-name-pattern="each refusal of a group link" test/social.test.js fail 1; pass at the tip
  • Live, two fresh homes on this tip + Bot Social 929d390 (what prod serves), both Chiefs room-only from a face room: mint → Join with Chief → host seats @chief.ben.home (ticket), guest has "Trip plan"; re-paste → claimed: true, joined: true. Homes from main 9f4aa52 with a stuck claim, upgraded to this tip: host Chief promoted at start, guest preview claimed: true, joined: false, Join again → seated.
  • gh pr checks 900 → tests-ok and every Test job green at this head

Not run: full npm run verify locally (CI ran it), smoke bench, Desktop, a real model.

Findings: no actionable findings.

  • A room-only identity now counts as none for a link (activeHandleOf), so identify → plugin register(forInvite) promotes it (existing Promoting a room-only Bot Social identity drops the stored owner name and home that the share envelope needs #863 path, no new server call). That is the documented link consent ("a link's own Bots get a full identity"), not a new grant: reach() still decides every send.
  • rt.identifyInviteBots runs once when serveInvites registers; a failure only warns. Withdrawn links that seated nobody wake nothing (tested).
  • joined is computed from local state only (an inbound-share room with that ticket and a local Bot seated); no peer-controlled text added to model-visible or trusted headers. The dialog's owner name is the already-cleaned preview owner.
  • Retry with other Bots still answers Bot Social's already_claimed sentence; acceptable, out of scope.

Disclosure: the same executor session that wrote this PR ran this review (owner asked for one focused review, speed first; Birfy is in selfReview). This is not merge authority by itself; the merge goes through enqueue-guard and the queue.

@github-actions github-actions Bot added the pass label Oct 24, 2026
@Birfy
Birfy added this pull request to the merge queue Oct 24, 2026
Merged via the queue into ThinkFlowLab:main with commit abaebb2 Oct 24, 2026
11 of 12 checks passed
@Birfy
Birfy deleted the fix-899-roomonly-link branch October 24, 2026 00:09
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Oct 24, 2026
HugoXK pushed a commit to HugoXK/FlyBot that referenced this pull request Oct 24, 2026
…unt's Verdict (ThinkFlowLab#944)

Owner: Dev
Reviewer: Reviewer

Fixes ThinkFlowLab#943

Written-by: Dev

## What changed

Both halves of ThinkFlowLab#943, each a rule the pipeline described and nothing
read.

**Part 1 — the two lines, before the queue.** `guard()`
(`.github/scripts/enqueue-guard.mjs`) now refuses a **non-draft**
request on a branch this repository owns when the first 20 lines of its
body carry no line starting `Owner:` or no line starting `Reviewer:`, or
when `Reviewer:` names a login outside the policy's `reviewers`. Each
refusal names the line, not just the defect: `missing line: Owner:`,
`missing line: Reviewer:`, `Reviewer names "x", who is not in
reviewers`. Twenty lines rather than two because an agent body opens
with its own HTML comment — the open ThinkFlowLab#941 carries `Owner:` on line 4 —
and a top-two check would refuse every such pull request. A `Reviewer:`
value is one login, comma-separated, `@` and any case allowed; a
`Reviewer:` with no value names nobody and is refused as the missing
line. A **draft** is not entering the queue and is not refused, and
nothing older is ever re-read: ThinkFlowLab#940's merged record stays exactly as it
merged. The mechanical release PR and the fork keep the exemptions they
already had.

**Part 2 — a bot account writes no conclusion.** `decide()`
(`.github/scripts/review-gate.mjs`) refuses a report authored by a
GitHub bot account — `user.type === 'Bot'`, or a login ending `[bot]` —
before the association test, with its own reason naming the account, so
the log says which defect fired. The report still joins `judged` with
`counts: false`. The rule is the **account type, not a login list**:
every Bot writes as the shared `Birfy` account, which is a `User`, and a
list containing it would void the whole pipeline. What voids such a
report is the real Reviewer's `Correction from Reviewer:` — the shape
the Reviewer already used on ThinkFlowLab#940
([6381646389](ThinkFlowLab#940 (comment))).
`review-gate`, `merge-gate` and `enqueue-guard` all inherit it because
they share `decide()`; the two API readers now carry `user.type`
alongside the association.

Docs: `AGENTS.md` (Merge gates: the queue bullet and `One conclusion per
tip`) and `docs/autonomous-dev.md` (`§ The reviewed tip` rule 6, and the
enqueue-guard sentence) state each rule once.

Not a user-visible change — the gate's own bookkeeping, same precedent
as ThinkFlowLab#912 and ThinkFlowLab#928 — so no `changelog.d/` fragment.

## INC at `87b49961` answered

Reviewer was right, and the line in the first version of this body was
false: "a merged pull request is never re-judged". `enqueue-guard.yml`
judges `issue_comment` too, so **any** sentence on a merged pull request
runs this check again and the two header reasons fired on a body nobody
can edit any more — the live list of merged pull requests without these
lines is ThinkFlowLab#926, ThinkFlowLab#925, ThinkFlowLab#908, ThinkFlowLab#901, ThinkFlowLab#900, ThinkFlowLab#898, ThinkFlowLab#897 (ThinkFlowLab#940 escaped only
because I edited its body afterwards).

The fix is the one Reviewer named: `decidePull` already reads
`pull.merged`, so `guard()` takes it and the body check is `if (!draft
&& !merged)`. The new test pins both halves — `ThinkFlowLab#943 INC: a merged pull
request is not re-judged when a comment arrives`: the same body (no
lines, a PASS at the tip) is green when `merged`, and still refused with
both `missing line:` reasons when it is an open non-draft request.
Mutation: dropping `&& !merged` reds exactly that test.

Checked against the real bodies, through the branch's `guard()`:

```text
ThinkFlowLab#926 merged true | merged=true ok: true | open ok: false | open reasons: missing line: Owner:; missing line: Reviewer:
ThinkFlowLab#925 merged true | merged=true ok: true | open ok: false | open reasons: missing line: Owner:; missing line: Reviewer:
ThinkFlowLab#908 merged true | merged=true ok: true | open ok: false | open reasons: missing line: Owner:; missing line: Reviewer:
ThinkFlowLab#897 merged true | merged=true ok: true | open ok: false | open reasons: missing line: Owner:; missing line: Reviewer:
```

`AGENTS.md` now says "a **merged** pull request is never re-judged: this
check also runs on every comment, and history is not a request", and
`docs/autonomous-dev.md` says the same next to the enqueue-guard
sentence.

## Evidence

Red first, on the pristine `c7286584` with only the new tests added (11
red, 3 controls): `.github/scripts/enqueue-guard.test.mjs` 36/7 — no
`Owner:`, no `Reviewer:`, `Reviewer:` outside `reviewers`, the lines
after the 20th, a `Reviewer:` with no value, a bot-account `Verdict:`,
and the INC case below; `.github/scripts/review-gate.test.mjs` 45/3;
`.github/scripts/merge-gate.test.mjs` 35/1. The three part-1 tests that
stay green there are the controls that catch over-refusal: an
`@`/mixed-case listed login, the two lines at line 4-5 (ThinkFlowLab#941's shape),
and a draft.

Seven mutations, each red on its own test and restored byte-identical:
drop `if (!draft)` → the draft control; `slice(0, 20)` → `slice(0, 2)` →
the line-4 test; drop the membership loop → the unlisted login; drop the
`Owner:` check; drop the `[bot]` half of the account test; allow an
empty `Reviewer:` value; drop the bot check in `decide`.

Gate 4 for part 2 replays ThinkFlowLab#940: `cursor[bot]`'s report at that tip is
`counts: false` with the bot-account reason, while `Birfy`'s report at
the same tip still counts — asserted in `review-gate.test.mjs` and again
through `merge-gate`'s queue entry.

At tip `23f14dbaaba6c283f2acca7259cfa99575f0ef74` (the branch head, no
commit after it):

```text
$ node --test ".github/scripts/*.test.mjs"
# tests 383 / pass 377 / fail 4 / skipped 2
```

The four failures are the `ds-bot-patches` format tests, which shell out
to `git diff`/`git apply`; this box has no `git` (the repo's own note in
`.github/scripts/fixtures/review-reports-660-663.json` says the same
four fail on main without it). Per file: `enqueue-guard` 42/42,
`review-gate` 48/48, `merge-gate` 36/36, `gate-check` 16/16 —
`gate-check`'s fixture pull bodies now carry the two lines, because the
guard it drives reads them. `node .github/scripts/changelog.mjs check` →
54 fragments well formed; `testing-layers.mjs --check` → 350 files in 15
workflows, all classified; `self-test-catalog.mjs --check` → page and
catalog agree; `oxlint` on the six touched JS files → 0 errors (one
pre-existing warning in `gate-check.test.mjs:101`, untouched).

Not run: `npm run verify` end to end (no `node_modules`/harness on this
box, and `arch-docs` needs `git log`); no owned subsystem file changed,
so `arch-docs` has nothing to pair here. CI's `tests-ok` and `package`
cover both.

## Risk

T3 by surface: `.github/scripts/` and the two policy documents. It
changes no product code path, no `core/`, no `patches/`, so the patch
round-trip and the client bundle are untouched; what it can do is refuse
a pull request that used to reach the queue, which is the point of it.
No bypass, no label, no history rewritten.

## Scope

Eight files: the two source scripts, four test files, the two docs. No
`core/`, no `patches/`, no generated file, so the patch round-trip is
untouched. The check is enqueue-time only — it is deliberately not added
to `review-gate`'s or `judge`'s conclusion tests, which read every
report on every pull request and would refuse reports on pull requests
that never had the two lines.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Group link join fails when a Chief holds a room-only identity; dialog then says 'already joined'

1 participant