Repository navigation
social: a read-only window draws no Sharing or members invite write - #2421
Conversation
…hinkFlowLab#2418) The Sharing card on a local Bot and the Members card of a local group all write: create-invite, share-with-friend, revoke-grant, room-invite-mint. A read-only window can only have them refused, so both cards read the reason instead and follow the rule ThinkFlowLab#2219 and ThinkFlowLab#2228 already set for the rest of the plugin.
|
@Birfy — review request for the queue (one comment, this tip only).
Request: a formal Evidence to re-run if you want it: red first on clean base Scope note for the design read: read-only is |
|
Verdict: PASS at d608f2c Ran at this tip: Red first (base blob of Review points:
Not run: full |
…wLab#2424) Fixes ThinkFlowLab#2422 Owner: Dev-2 Written-by: Dev-2 Reviewer: Birfy Claim-Id: 13aab537f38444fcb39498eeaba6528a Branch: dev/2-approval-reason-visible Tip: a9cbe93 Tree: 90a717a Base: 16d1645 Diff: 20 files, +543/−105 (hand-written 11 = +213/−6; generated 9 = +330/−99) Tier: T2 (why: []) ## What changed The reason a hold was made with was only ever a hover title, and only on one of the two cards that draw the same hold. `FlyBotApprovals` (`core/src/client/flybot-slots.js`) put the policy's sentence on the card wrapper — `h('div', { className: 'bt-flybot-approval', title: approval.reason ?? undefined }, …)` — and drew nothing of it in the body, so a window whose pointer never rests there, a screen reader, and a phone that cannot hover never read why the request waits; the read-only form of that card draws no buttons, so it carried no trace of the reason at all. The held-reply bubble could not draw it even if it wanted to: `heldOf()` (`core/src/host/hooks.js`) never mapped the hold's `reason` onto the record the client gets, so the two surfaces disagreed about the same hold. Both cards now draw it as its own line, `bt-flybot-approval-reason`, under the `Sender → Recipient` line, through one shared helper: `reasonLine(reason)` in the new `core/src/client/reason-line.js` answers `{ text, title }` or `null`. The visible text is bounded to 200 code points with a trailing `…` (counted on code points, so an emoji stays whole), the whole sentence stays in that line's `title`, and a reason that is empty or only whitespace answers `null` — no line, no empty title — so a hold with no reason looks exactly as it did. The wrapper's own `title` is gone from the dock card, because the sentence is no longer hidden there. `heldOf()` gains one property, `...(a.reason ? { reason: a.reason } : {})`, its third optional field beside `toOwner` and `error`. The new file lives in `core/src/client/` rather than `core/src/shared/`: `risk-rules.json` scores `core/src/shared/**` T3, which would lift the whole slice out of T2 for no product reason. Rebased onto `16d16456` (after `ThinkFlowLab#2410` and `ThinkFlowLab#2421`); `ThinkFlowLab#2410` touches the same three files, so its head's changes are kept and this slice stacks on them — the card's head line still carries `ThinkFlowLab#2409`'s age (`Waiting for approval · 12 min`) and the reason line sits under the origin line. `core/client.js` and the `0008` patch were rebuilt with `npm --prefix core run build` + `node scripts/ds-bot-patches.mjs generate`, never hand-edited. ## Red first On a clean `16d16456` worktree with only the three test files from this branch, per file (log `tmp/p538-red-16d16456.log`): - `core/test/approval-reason.test.js` — 5 tests / 1 pass / **4 fail** / 0 cancelled: the dock card draws no `bt-flybot-approval-reason` (`expected 1, actual 0`), the 200-code-point bound draws `''`, the read-only card keeps no reason (`expected 1, actual 0`), and the held-reply bubble draws no line (`expected 1, actual 0`). The passing one is the control: a hold with no reason, an empty one and a blank one draw nothing on the base too. - `core/test/held-reply-seam.test.js` — 3 tests / 2 pass / **1 fail**: `expected: 'held by test'`, the view's record carries no `reason`. - `core/test/overlay-approvals.test.js` — 7 tests / 6 pass / **1 fail**: the `ThinkFlowLab#260` card, drawn from the dock's own record, carries the reason only as `"title":"messages to @chief.uia.home's Bots need your approval"` on the wrapper and no reason node. With the fix the same three files are 5/5, 3/3 and 7/7 on the same base. ## Ran ``` TZ=UTC ARCH_DOCS_BASE=16d16456 npm run verify node .github/scripts/changelog.mjs check node .github/scripts/check-empty-catch.mjs --check node .github/scripts/check-mojibake.mjs --check node scripts/ds-bot-patches.mjs generate (twice: the second run leaves the tree clean) node scripts/ds-bot-patches.mjs canonical ``` 4848 = 2856+574+132+141+411+671+37+26, 0 fail / 0 cancelled; lint 0 errors (core 77 warnings, plugins 36/9/2/16); `arch-docs: OK (20 changed file(s))`; 10 patches apply cleanly, `core/` equals ds-bot + patches, and `generate` is a no-op on the second run; `build-client --check` exit 0; `canonical` ok. ## Not run The smoke test (this touches neither `index.js` nor a host route), CI's `package` job (pack check, typecheck, actionlint), and CI itself. Docs: `docs/subsystems/core-client.md` (both cards' bullets), `docs/subsystems/core-host.md` (`heldReplies` now carries the hold's `reason`), `docs/subsystems/plugin-runtime.md` (`heldOf()` and both card shapes point at `reasonLine`). <!-- flybot:checkpoint --> { "pr": 2424, "claim": "Dev-2", "issue": 2422, "summary": "A hold's reason lived only in the dock card's wrapper title and never reached the held-reply bubble, whose record dropped it in heldOf(); both cards now draw one bt-flybot-approval-reason line through client/reason-line.js reasonLine() — 200 code points plus an ellipsis visible, the whole sentence in the title, nothing for an empty or blank reason. heldOf() carries the hold's reason so the bubble draws the same line, the wrapper title is gone, and ThinkFlowLab#2410's age line stays. Bundle and 0008 regenerated.", "files": [ "changelog.d/2422-approval-reason-visible.fixed.md", "core/src/client/reason-line.js", "core/src/client/flybot-slots.js", "core/src/client/held-reply.js", "core/src/host/hooks.js", "core/test/approval-reason.test.js", "core/test/held-reply-seam.test.js", "core/test/overlay-approvals.test.js", "docs/subsystems/core-client.md", "docs/subsystems/core-host.md", "docs/subsystems/plugin-runtime.md", "core/client.js" ], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=16d16456 npm run verify", "result": "green", "at": "a9cbe93feaaea69266b743770015516a676bc95f" }, "next": "draft on purpose, waiting for Test and the first review" } <!-- /flybot:checkpoint -->
social: a read-only window draws no Sharing or members invite write
Fixes #2418
Owner: Dev-4
Reviewer: Birfy
Written-by: Dev-4
Claim-Id: 642b54ba1a2843b9bdb399bb636d9e60
Branch: dev/4-social-invite-readonly
Tip: d608f2c
Tree: 8798dcfb21e7e860e932006e5c6bd5da253b9479
Base: dd9c5a0
Diff: 4 files, +82/−4
Tier: T2
why: [] — the highest path is
plugins/social/client.jsat T2 under the liverisk-rules.jsonat this base;plugins/*/test/**andchangelog.d/**are T1,docs/**is T0Docs-changed: docs/subsystems/social.md (the read-only paragraph now names the two cards)
What was wrong
A read-only window still drew two social cards whose every control writes, and the host refuses all of them with
bot/read-only(core/src/host/channel.js:308):plugins/social/client.js:3487BotSocialDetails) tookrosterand then dropped it at:3493, soOwnBotSharing(:3498) had no window state at all. A probe with the read-only roster drewinvite-to-bot,mint-invite,share-with-friendandmanage-access, plus the buttons Copy invite link for Alice, Share with a friend and People with access (Revoke), i.e.social/create-invite,social/share-with-friendandsocial/revoke-grant.plugins/social/client.js:1166LocalInviteRow) had the same gap: a probe drewroom-invite-mintand the button Create link, i.e.host('room-invite-mint').Both cards are the same class as #2219 (the sidebar's notices and
+menu), #2228 (the room overview) and #2400 (the group overview): the window is named inroster.readOnly, the writes can only be refused, and the rest of the plugin already reads that state.What changed
plugins/social/client.js(+17/−3):BotSocialDetailshandsrostertoOwnBotSharing, which returns the window's own sentence in asharing-readonlyrow instead of the three write panels;LocalInviteRowtakesrosterand returns the sentence in aroom-invite-readonlyrow instead of the invite row. A writable window draws exactly what it drew before; nothing is opened or read to decide this, so no journal or settings file is touched.plugins/social/test/social-readonly.test.js(+63): four tests, two per card — the read-only window draws no write control and still says why, the writable window keeps every control.docs/subsystems/social.md(+1/−1): the read-only paragraph now names the two cards.changelog.d/2418-social-invite-readonly.fixed.md(+1).What I ran
dd9c5a05+ only this test file:plugins/social/test/social-readonly.test.js12 tests, 10 pass, 2 fail —not ok 9(Sharing still drewmint-invite,share-with-friend,manage-access) andnot ok 11(Members still drewroom-invite-mint); the two "a writable window keeps them" tests already pass at the base, so the test is not passed by deleting the card.TZ=UTC ARCH_DOCS_BASE=dd9c5a05 npm run verify— exit 0, 4836/4836 tests, 0 fail (core 2844, social 574, tree 132, timeline 140, workflow 411,.github+policy 671/37, interop 26);arch-docs: OK (4 changed file(s)); lint 76/36/9/2/16 warnings, 0 errors.node scripts/ds-bot-patches.mjs checkexit 0 (ds-bot/pin, 10 patches,core/= pin + patches);generatetwice, exit 0 both, no diff — nothing underpatches/orcore/client.jsmoved.node .github/scripts/check-mojibake.mjs --check(inside verify) and the changelog check pass.Not run
core/index.jsorcore/src/host/changed.Tip
6adbf1cff5was built by the push helper from the same tree (8798dcfb) those checks ran on (d7b47a1f).Checkpoint
{ "pr": 2421, "claim": "Dev-4", "issue": 2418, "summary": "Bot settings → Sharing and a local group's Members card drew their write controls in a read-only window: the invite maker, Share with a friend, People with access and the room invite's Create link all call a write the host refuses there. Both cards now read the window's reason instead, and a writable window is unchanged. The tests mount the real client and count what each window state draws. The plugin's read-only paragraph in the subsystem doc names the two cards.", "files": ["plugins/social/client.js", "plugins/social/test/social-readonly.test.js", "docs/subsystems/social.md", "changelog.d/2418-social-invite-readonly.fixed.md"], "checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=dd9c5a05 npm run verify; node scripts/ds-bot-patches.mjs check; node scripts/ds-bot-patches.mjs generate twice", "result": "green", "at": "d7b47a1f9baf6056a2f276454434a81a1d7a8f6b" }, "next": "none — ready for review" }