docs: Phase 180 v0.8.145 roadmap, parity, and feature specs

Mark Phase 180 complete and plan Phase 181. Document aria-owns, announcement
labels, status role bundle, and root plain vs dual mention buckets across
SEARCH, CHANNEL_CATEGORIES, NOTIFICATIONS, and IPC.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-22 19:57:18 -04:00
co-authored by Cursor
parent 00f1c9d4e8
commit 78888e4c8e
10 changed files with 47 additions and 16 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# Pearcord architecture # Pearcord architecture
**Current release:** v0.8.144 (desktop app `apps/pearcord`). Phase history and checklists live in [PLATFORM_ROADMAP.md](./PLATFORM_ROADMAP.md). Gap vs a centralized chat product is tracked in [PEARCORD_PARITY.md](./PEARCORD_PARITY.md). **Current release:** v0.8.145 (desktop app `apps/pearcord`). Phase history and checklists live in [PLATFORM_ROADMAP.md](./PLATFORM_ROADMAP.md). Gap vs a centralized chat product is tracked in [PEARCORD_PARITY.md](./PEARCORD_PARITY.md).
Pearcord is a **100% peer-to-peer** community chat client on [Pear](https://pear.holepunch.to) / Bare. There is no Pearcord-operated message server: guilds, DMs, discovery listings, and bot events replicate over **Hyperswarm** topics with **protomux** framing. Pearcord is a **100% peer-to-peer** community chat client on [Pear](https://pear.holepunch.to) / Bare. There is no Pearcord-operated message server: guilds, DMs, discovery listings, and bot events replicate over **Hyperswarm** topics with **protomux** framing.
+5 -1
View File
@@ -442,7 +442,11 @@ Pearcord validates itself without manual QA.
| `npm run test:channel-category-forum-label` | Forum category label (`CHANNEL_CATEGORY_FORUM_LABEL_SMOKE_OK`) | | `npm run test:channel-category-forum-label` | Forum category label (`CHANNEL_CATEGORY_FORUM_LABEL_SMOKE_OK`) |
| `npm run test:notif-mark-all-status-labelledby` | Status labelledby (`NOTIF_MARK_ALL_STATUS_LABELLEDBY_SMOKE_OK`) | | `npm run test:notif-mark-all-status-labelledby` | Status labelledby (`NOTIF_MARK_ALL_STATUS_LABELLEDBY_SMOKE_OK`) |
| `npm run test:guild-mention-triple-bucket-pick` | Mention triple bucket (`GUILD_MENTION_TRIPLE_BUCKET_PICK_SMOKE_OK`) | | `npm run test:guild-mention-triple-bucket-pick` | Mention triple bucket (`GUILD_MENTION_TRIPLE_BUCKET_PICK_SMOKE_OK`) |
| `npm run test:boot` | `pear run` pre-run (`BOOT_OK`) then Phase 124179 smokes + `test:companion-boot` | | `npm run test:search-recent-chip-owns` | Chip aria-owns (`SEARCH_RECENT_CHIP_OWNS_SMOKE_OK`) |
| `npm run test:channel-category-announcement-label` | Announcement label (`CHANNEL_CATEGORY_ANNOUNCEMENT_LABEL_SMOKE_OK`) |
| `npm run test:notif-mark-all-status-role` | Status role bundle (`NOTIF_MARK_ALL_STATUS_ROLE_SMOKE_OK`) |
| `npm run test:guild-root-plain-triple-mention-pick` | Root plain triple mention (`GUILD_ROOT_PLAIN_TRIPLE_MENTION_PICK_SMOKE_OK`) |
| `npm run test:boot` | `pear run` pre-run (`BOOT_OK`) then Phase 124180 smokes + `test:companion-boot` |
| `npm run test:server-folder-prefs-mesh` | Live folder prefs mesh (optional; run manually — can exceed 150s on DHT) | | `npm run test:server-folder-prefs-mesh` | Live folder prefs mesh (optional; run manually — can exceed 150s on DHT) |
| `npm run test:profile` | Profile update + persist (`PROFILE_SMOKE_OK`) | | `npm run test:profile` | Profile update + persist (`PROFILE_SMOKE_OK`) |
| `npm run test:e2e` | Full journey host+guest (`E2E_SMOKE_OK`) | | `npm run test:e2e` | Full journey host+guest (`E2E_SMOKE_OK`) |
+1
View File
@@ -23,6 +23,7 @@ Gossip: existing `RPC.CHANNEL_CREATE` carries full channel row including `parent
## UI ## UI
- Announcement-only categories use “announcement channel(s)” in `aria-label`; mixed categories can include announcement counts (v0.8.145). Smoke: `npm run test:channel-category-announcement-label`.
- Forum-only categories use “forum channel(s)” in `aria-label`; mixed categories can include forum counts (v0.8.144). Smoke: `npm run test:channel-category-forum-label`. - Forum-only categories use “forum channel(s)” in `aria-label`; mixed categories can include forum counts (v0.8.144). Smoke: `npm run test:channel-category-forum-label`.
- Voice-only categories use “voice channel(s)” in `aria-label`; mixed categories include a voice count (v0.8.143). Smoke: `npm run test:channel-category-voice-label`. - Voice-only categories use “voice channel(s)” in `aria-label`; mixed categories include a voice count (v0.8.143). Smoke: `npm run test:channel-category-voice-label`.
- Category `aria-label` includes channel count when there is no unread or mention activity (v0.8.142). Smoke: `npm run test:channel-category-label-count`. - Category `aria-label` includes channel count when there is no unread or mention activity (v0.8.142). Smoke: `npm run test:channel-category-label-count`.
+1 -1
View File
@@ -1,6 +1,6 @@
# Pearcord documentation index # Pearcord documentation index
All project documentation lives in **`pearcord-docs`** (`/Users/raven/dev/pearcord/docs/`). **Release:** v0.8.144. All project documentation lives in **`pearcord-docs`** (`/Users/raven/dev/pearcord/docs/`). **Release:** v0.8.145.
| Start here | Document | | Start here | Document |
|------------|----------| |------------|----------|
+2 -2
View File
@@ -31,7 +31,7 @@ JSON lines over `pear-pipe` between `ui/app.js` and `apps/pearcord/index.js`. Ha
### Channel pick order (`select-guild` / `loadGuild`) ### Channel pick order (`select-guild` / `loadGuild`)
When a guild opens, `pearcord-platform` `_selectGuildChannelWithUnread` chooses the active text/forum/thread/announcement channel in this order (v0.8.144): When a guild opens, `pearcord-platform` `_selectGuildChannelWithUnread` chooses the active text/forum/thread/announcement channel in this order (v0.8.145):
1. **Unread + mentions** — score `mentions × 1000 + unread` per channel. Pool selection: 1. **Unread + mentions** — score `mentions × 1000 + unread` per channel. Pool selection:
- If every unread channel shares one category (`parentId`), only channels in that category compete (mention-weighted score, then lower `position` on ties). Smokes: `test:guild-single-category-pool`, `test:guild-mention-single-category-pick`, `test:guild-dual-category-isolation-pick`. - If every unread channel shares one category (`parentId`), only channels in that category compete (mention-weighted score, then lower `position` on ties). Smokes: `test:guild-single-category-pool`, `test:guild-mention-single-category-pick`, `test:guild-dual-category-isolation-pick`.
@@ -39,7 +39,7 @@ When a guild opens, `pearcord-platform` `_selectGuildChannelWithUnread` chooses
2. **`lastChannelByGuild`** — USER_PREFS last viewed channel for this guild when valid and viewable. 2. **`lastChannelByGuild`** — USER_PREFS last viewed channel for this guild when valid and viewable.
3. **First text channel** — lowest-position `text` channel, else first candidate. 3. **First text channel** — lowest-position `text` channel, else first candidate.
Smokes: `test:guild-channel-pick-order` (IPC + platform source), `test:guild-unread-position-tie`, `test:guild-in-bucket-position-tie`, `test:guild-mention-in-bucket-pick`, `test:guild-mention-single-category-pick`, `test:guild-dual-category-isolation-pick`, `test:guild-mixed-bucket-pick`, `test:guild-mention-root-bucket-pick`, `test:guild-root-mention-bucket-pick`, `test:guild-mention-triple-bucket-pick`, `test:guild-bucket-minpos-root-pick`, `test:guild-bucket-minpos-order-pick`, `test:guild-bucket-minpos-triple-pick`, `test:guild-bucket-total-over-minpos`, `test:guild-uncategorized-bucket-pick`, `test:guild-single-category-pool`, `test:guild-category-unread-pick`, `test:guild-category-bucket-pick`, `test:guild-mention-bucket-pick`, `test:guild-bucket-minpos-tie`, `test:guild-mention-channel-pick`, `test:guild-unread-over-last-channel`, `test:guild-last-channel-only-load`, `test:guild-no-unread-last-channel`, `test:guild-first-text-fallback`. Smokes: `test:guild-channel-pick-order` (IPC + platform source), `test:guild-unread-position-tie`, `test:guild-in-bucket-position-tie`, `test:guild-mention-in-bucket-pick`, `test:guild-mention-single-category-pick`, `test:guild-dual-category-isolation-pick`, `test:guild-mixed-bucket-pick`, `test:guild-mention-root-bucket-pick`, `test:guild-root-mention-bucket-pick`, `test:guild-mention-triple-bucket-pick`, `test:guild-root-plain-triple-mention-pick`, `test:guild-bucket-minpos-root-pick`, `test:guild-bucket-minpos-order-pick`, `test:guild-bucket-minpos-triple-pick`, `test:guild-bucket-total-over-minpos`, `test:guild-uncategorized-bucket-pick`, `test:guild-single-category-pool`, `test:guild-category-unread-pick`, `test:guild-category-bucket-pick`, `test:guild-mention-bucket-pick`, `test:guild-bucket-minpos-tie`, `test:guild-mention-channel-pick`, `test:guild-unread-over-last-channel`, `test:guild-last-channel-only-load`, `test:guild-no-unread-last-channel`, `test:guild-first-text-fallback`.
| `select-channel` | `channelId` | Set active text channel (voice IDs join voice) | | `select-channel` | `channelId` | Set active text channel (voice IDs join voice) |
| `join-voice` | `channelId` | Join voice channel + gossip | | `join-voice` | `channelId` | Join voice channel + gossip |
+1
View File
@@ -23,6 +23,7 @@ Skipped when:
- **Channel list** — existing unread + `@` pills - **Channel list** — existing unread + `@` pills
- **🔔 header** — inbox panel, prefs, mark all read; **click row jumps to message** (v0.8.35+) - **🔔 header** — inbox panel, prefs, mark all read; **click row jumps to message** (v0.8.35+)
- **Mark all read** — dedicated header button when unread rows exist (v0.8.115); smoke: `npm run test:notif-mark-all-header` - **Mark all read** — dedicated header button when unread rows exist (v0.8.115); smoke: `npm run test:notif-mark-all-header`
- **Status role bundle (v0.8.145)** — mark-all status uses `role="status"` with `aria-live="polite"`, `aria-atomic`, and `aria-relevant="additions"`. Smoke: `npm run test:notif-mark-all-status-role`
- **Status labelledby (v0.8.144)** — mark-all status banner sets `aria-labelledby` to the header button id. Smoke: `npm run test:notif-mark-all-status-labelledby` - **Status labelledby (v0.8.144)** — mark-all status banner sets `aria-labelledby` to the header button id. Smoke: `npm run test:notif-mark-all-status-labelledby`
- **Status describedby target (v0.8.143)** — only the mark-all button references `notif-mark-all-status` via `aria-describedby`; the status banner does not self-reference. Smoke: `npm run test:notif-mark-all-status-describedby-only` - **Status describedby target (v0.8.143)** — only the mark-all button references `notif-mark-all-status` via `aria-describedby`; the status banner does not self-reference. Smoke: `npm run test:notif-mark-all-status-describedby-only`
- **Disabled keeps describedby (v0.8.142)** — after mark-all, a disabled header button keeps `aria-describedby="notif-mark-all-status"` when the status banner already exists. Smoke: `npm run test:notif-mark-all-disabled-describedby` - **Disabled keeps describedby (v0.8.142)** — after mark-all, a disabled header button keeps `aria-describedby="notif-mark-all-status"` when the status banner already exists. Smoke: `npm run test:notif-mark-all-disabled-describedby`
+13 -4
View File
@@ -4,14 +4,23 @@ Gap analysis between **Pearcord** (P2P Pear app) and a typical **centralized cha
> This is the only doc that compares against an external reference product. All other docs use Pearcord naming only. > This is the only doc that compares against an external reference product. All other docs use Pearcord naming only.
## Summary (v0.8.145)
| Area | Reference product | Pearcord | Gap |
|------|-------------------|----------|-----|
| Search | Recent queries | controls + owns + activedescendant | autocomplete (P181); no cloud |
| Channels | Categories | Voice/forum/announcement labels | Stage label (P181) |
| Notifications | Inbox | Status role + labelledby + describedby | Status live smoke (P181) |
| Guild open | Channel pick | Plain vs mention bucket scoring | Mixed bucket (P181) |
## Summary (v0.8.144) ## Summary (v0.8.144)
| Area | Reference product | Pearcord | Gap | | Area | Reference product | Pearcord | Gap |
|------|-------------------|----------|-----| |------|-------------------|----------|-----|
| Search | Recent queries | controls + activedescendant + chips | aria-owns (P180); no cloud | | Search | Recent queries | controls + activedescendant + chips | aria-owns (done P180); no cloud |
| Channels | Categories | Voice/forum/text label semantics | Announcement label (P180) | | Channels | Categories | Voice/forum/text label semantics | Announcement label (done P180) |
| Notifications | Inbox | Status labelledby + describedby | Status role bundle (P180) | | Notifications | Inbox | Status labelledby + describedby | Status role bundle (done P180) |
| Guild open | Channel pick | Mention beats multi plain buckets | Root plain triple (P180) | | Guild open | Channel pick | Mention beats multi plain buckets | Root plain triple (done P180) |
## Summary (v0.8.143) ## Summary (v0.8.143)
+20 -6
View File
@@ -881,16 +881,29 @@ When completing work, change `[ ]` → `[x]` and add a one-line note under **Cha
--- ---
### Phase 180 — Inbox & guild depth (planned) ### Phase 180 — Inbox & guild depth (v0.8.145)
| ID | Item | Status | | ID | Item | Status |
|----|------|--------| |----|------|--------|
| P180-1 | Search: listbox `aria-owns` mirrors controls link | [ ] | | P180-1 | Search: listbox `aria-owns` mirrors controls link | [x] |
| P180-2 | Channels: announcement category label count | [ ] | | P180-2 | Channels: announcement category label count | [x] |
| P180-3 | Notifications: mark-all status `role="status"` smoke bundle | [ ] | | P180-3 | Notifications: mark-all status `role="status"` smoke bundle | [x] |
| P180-4 | Guild: smoke root plain triple beats two category mentions | [ ] | | P180-4 | Guild: smoke root plain triple beats two category mentions | [x] |
**Next:** Chip aria-owns · announcement label · status role smoke · root plain triple. **Next:** ~~Chip aria-owns · announcement label · status role smoke · root plain triple~~ (Phase 180).
---
### Phase 181 — Search & guild polish (planned)
| ID | Item | Status |
|----|------|--------|
| P181-1 | Search: combobox `aria-autocomplete` on search field | [ ] |
| P181-2 | Channels: stage category label count | [ ] |
| P181-3 | Notifications: mark-all status `aria-live` assert smoke | [ ] |
| P181-4 | Guild: smoke combined mention+plain bucket scoring | [ ] |
**Next:** Search autocomplete · stage label · status live smoke · mixed bucket score.
--- ---
@@ -2323,6 +2336,7 @@ When completing work, change `[ ]` → `[x]` and add a one-line note under **Cha
### Changelog ### Changelog
- **2026-05-22** — v0.8.145: Phase 180 — Chip aria-owns, announcement label, status role bundle, root plain triple mention (`test:search-recent-chip-owns`, `test:channel-category-announcement-label`, `test:notif-mark-all-status-role`, `test:guild-root-plain-triple-mention-pick`). Phase 181 planned.
- **2026-05-22** — v0.8.144: Phase 179 — Search controls link, forum label, status labelledby, mention triple bucket (`test:search-recent-chip-controls`, `test:channel-category-forum-label`, `test:notif-mark-all-status-labelledby`, `test:guild-mention-triple-bucket-pick`). Phase 180 planned. - **2026-05-22** — v0.8.144: Phase 179 — Search controls link, forum label, status labelledby, mention triple bucket (`test:search-recent-chip-controls`, `test:channel-category-forum-label`, `test:notif-mark-all-status-labelledby`, `test:guild-mention-triple-bucket-pick`). Phase 180 planned.
- **2026-05-22** — v0.8.143: Phase 178 — Chip activedescendant, voice label, status describedby only, triple bucket minPos (`test:search-recent-chip-activedescendant`, `test:channel-category-voice-label`, `test:notif-mark-all-status-describedby-only`, `test:guild-bucket-minpos-triple-pick`). Phase 179 planned. - **2026-05-22** — v0.8.143: Phase 178 — Chip activedescendant, voice label, status describedby only, triple bucket minPos (`test:search-recent-chip-activedescendant`, `test:channel-category-voice-label`, `test:notif-mark-all-status-describedby-only`, `test:guild-bucket-minpos-triple-pick`). Phase 179 planned.
- **2026-05-22** — v0.8.142: Phase 177 — Alt+Delete chip, label channel count, disabled describedby, bucket minPos order (`test:search-recent-chip-alt-delete`, `test:channel-category-label-count`, `test:notif-mark-all-disabled-describedby`, `test:guild-bucket-minpos-order-pick`). Phase 178 planned. - **2026-05-22** — v0.8.142: Phase 177 — Alt+Delete chip, label channel count, disabled describedby, bucket minPos order (`test:search-recent-chip-alt-delete`, `test:channel-category-label-count`, `test:notif-mark-all-disabled-describedby`, `test:guild-bucket-minpos-order-pick`). Phase 178 planned.
+1 -1
View File
@@ -2,7 +2,7 @@
Documentation for **[Pearcord](https://git.ssh.surf/pearcord)** — a 100% peer-to-peer community chat app on [Pear](https://pear.holepunch.to). No central message servers. Documentation for **[Pearcord](https://git.ssh.surf/pearcord)** — a 100% peer-to-peer community chat app on [Pear](https://pear.holepunch.to). No central message servers.
**Current release:** v0.8.144 (desktop app `apps/pearcord`). **Current release:** v0.8.145 (desktop app `apps/pearcord`).
--- ---
+2
View File
@@ -56,6 +56,8 @@ See also [FORUM_ARCHIVE_SEARCH.md](./FORUM_ARCHIVE_SEARCH.md).
**v0.8.115:** Guild-scoped recent queries persist in `userPrefs.searchRecentByGuild[guildId]` (max 8, deduped). `setSearch` records queries ≥2 chars. Desktop shows **recent chips** under the search input when scope is `guild` and the field is empty. Smokes: `npm run test:search-recent-prefs`, `npm run test:members-online-sort` (UI wiring). **v0.8.115:** Guild-scoped recent queries persist in `userPrefs.searchRecentByGuild[guildId]` (max 8, deduped). `setSearch` records queries ≥2 chars. Desktop shows **recent chips** under the search input when scope is `guild` and the field is empty. Smokes: `npm run test:search-recent-prefs`, `npm run test:members-online-sort` (UI wiring).
**v0.8.145:** `#channel-search` and `#search-recent` cross-link via `aria-controls` and reciprocal `aria-owns`. Smoke: `npm run test:search-recent-chip-owns`.
**v0.8.144:** `#channel-search` sets `aria-controls="search-recent"`, `aria-expanded`, and `aria-haspopup="listbox"` while recent chips are visible. Smoke: `npm run test:search-recent-chip-controls`. **v0.8.144:** `#channel-search` sets `aria-controls="search-recent"`, `aria-expanded`, and `aria-haspopup="listbox"` while recent chips are visible. Smoke: `npm run test:search-recent-chip-controls`.
**v0.8.143:** Recent-chip listbox sets `aria-activedescendant` to the focused option id (`search-recent-opt-N`) on roving focus. Smoke: `npm run test:search-recent-chip-activedescendant`. **v0.8.143:** Recent-chip listbox sets `aria-activedescendant` to the focused option id (`search-recent-opt-N`) on roving focus. Smoke: `npm run test:search-recent-chip-activedescendant`.