feat: blocklist display + introspection — *KB Sharing* Blocked view (closes #162) - #189
Merged
Merged
Conversation
…loses #162) PR3 of the ADR-039 A2 local self-protection blocklist — closes #162. PR1 (#186) landed daemon enforcement; PR2 (#187) the human+AI action surface. This adds the display + introspection. Why a fetch (not the local replica): membership renders from the editor's local `kbc:` replica, but the blocklist is LOCAL-only on the daemon and never in the synced collection — so the editor cannot derive it and must fetch the authoritative state. An optimistic local mirror would miss blocks set in a prior session or by another frontend on a shared daemon. Daemon: - DocStore::all_kb_blocklists() + a read-only `kb/blocklist` query RPC returning `{ blocklist: { kb_id: [fp, ...] } }` (optional kb_id scopes to one). The ONLY way a client learns the local-only blocklist. Editor: - CollabState.kb_blocklists cache; a `send_blocklist_fetch` helper sent ON CONNECT (durable prior-session blocks) and AFTER each block/unblock (the user's own changes); reply → PendingResponseKind::Blocklist → CollabEvent::BlocklistUpdated → cache + repaint. Display-only; the daemon stays authoritative. - KbSharingEntry.blocked: Vec<BlockedView> in build_snapshot (label best-effort from the member replica; a non-member block falls back to the short fingerprint). - *KB Sharing* buffer: a foldable "Blocked (N)" section after Pending; at-point `B` → unblock (kb-sharing-unblock, not owner-gated); diagnostic.error theme key. - Introspection: all three actors share build_snapshot, so `blocked` flows into the kb_sharing_status MCP tool and (kb-sharing-status) Scheme primitive (#3/#8). Tests: daemon block-fence test extended with the kb/blocklist query oracle; editor blocklist_renders_blocked_view_with_member_label (bob stays a MEMBER and is listed Blocked with his label; a non-member block has empty label; the view emits a BlockedHeader + one Blocked row per principal). Daemon 120+51 tests + clippy clean; editor fmt + clippy --workspace + test --workspace green (incl GUI). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
What
PR 3 of 3 for #162 (ADR-039 A2 local blocklist) — closes the issue. PR1 (#186, merged) = daemon enforcement; PR2 (#187) = human+AI action surface. This adds the display + introspection: a
*KB Sharing*Blocked section, at-point unblock, and the blocked principals surfaced inkb_sharing_status/(kb-sharing-status).Why a fetch (not the local replica)
Membership renders from the editor's local
kbc:replica — but the blocklist is local-only on the daemon and never in the synced collection (that's the design). So the editor can't derive it; it must fetch the authoritative state. An optimistic local mirror would miss blocks set in a prior session or by another frontend on a shared daemon.Daemon
DocStore::all_kb_blocklists()+ a read-onlykb/blocklistquery RPC →{ blocklist: { kb_id: [fp, …] } }(optionalkb_idscopes to one). This is the ONLY way a client learns the local-only blocklist.Editor
CollabState.kb_blocklistscache; asend_blocklist_fetchhelper sent on connect (durable prior-session blocks) and after each block/unblock (the user's own changes); reply →PendingResponseKind::Blocklist→CollabEvent::BlocklistUpdated→ cache + repaint. Display-only; the daemon stays authoritative.KbSharingEntry.blocked: Vec<BlockedView>inbuild_snapshot(label resolved best-effort from the member replica; a non-member block falls back to the short fingerprint).*KB Sharing*buffer: a foldable Blocked (N) section after Pending; at-pointB→ unblock (kb-sharing-unblock, not owner-gated);diagnostic.errortheme key.build_snapshot, theblockedlist flows automatically into thekb_sharing_statusMCP tool and(kb-sharing-status)Scheme primitive (CLAUDE.md chore(deps): Update crossterm requirement from 0.28 to 0.29 #3/ci(deps): Bump the ci-dependencies group with 2 updates #8).Tests
kb/blocklistquery oracle (reports exactly the blocked principal).blocklist_renders_blocked_view_with_member_label— bob stays a member (a local block is not a removal) and is listed Blocked with his label; a non-member block has an empty label; the buffer view emits aBlockedHeader+ oneBlockedrow per principal.Verification
fmt --check+clippy --workspace --all-targets -D warningsclean (incl GUI);cargo test --workspacegreen.Closes #162.
🤖 Generated with Claude Code