TODO (ConcreteSky)
Maps the full feature backlog for a Bluesky community groups PHP package, from done items to planned panels and parity targets.
What this file does
Maps the full feature backlog for a Bluesky community groups PHP package, from done items to planned panels and parity targets.
When to use it
- Planning a PHP-first Bluesky integration with Web Components
- Building a multi-panel SPA with cache-first UX and SQLite
- Tracking parity with Facebook Groups and Bluesky client features
- Organising a package roadmap with prioritised UI and backend tasks
Assumes this stack
TODO (ConcreteSky)
This is the top-level TODO for the package (GitHub-facing).
North Star
Build “Facebook Groups, but for Bluesky”: community spaces (membership + moderation + discovery + admin tooling) powered by AT Protocol identities and content, delivered as a PHP-first package with Web Components so it can embed into most PHP websites with minimal integration.
Principles:
- Web Components first: the UI should be portable across PHP sites.
- One shared controller per behavior: panels stay thin; reusable logic lives in
js/controllers/*. - Durable background work: rate-limit and maintenance work should keep running via ConcreteCMS jobs (and later, cron for non-Concrete hosts).
- Cache-first UX: use SQLite for fast browsing/filtering; fall back to network when needed.
Parity targets
- Bluesky client parity: core posting, reading, engagement, moderation, and account management.
- Facebook Groups parity: group spaces, membership/roles, moderation queue, units/guides, events, announcements, and insights.
Already done (implemented)
- Tabs/panels system with persistence (
bsky_active_tabsin localStorage +#tabs=...hash) + drag ordering + resize. - SQLite cache (profiles, follower/following snapshots + diffs, notifications, posts) with migrations + schema versioning.
- Cache/ops endpoints:
cacheStatus,cacheSync,cacheSyncRecent,cacheQueryPeople,cacheFriendDiff,cacheQueryNotifications,cacheQueryMyPosts. - Calendar coverage API + UI:
cacheCalendarMonth+ DB Manager calendar view. - Social HUD: cache coverage calendar icon (ring indicator: up-to-date / partial / needs update + pre-join grey), click day for summary + backfill actions.
- Shared cache settings lightbox: usable from Social HUD and notification bar settings; leverages DB Manager calendar for drill-down + range backfill tooling.
- Backfill tooling:
- Posts:
cacheBackfillMyPosts - Notifications:
cacheBackfillNotifications - UI wiring exists (DB Manager + Cache Status components)
- Posts:
- “Month-level bulk actions” via calendar selection + range backfill/refresh.
No longer needed / superseded
- Separate “Search tab” (superseded by in-panel search/filtering and the shared search utilities; consider adding a dedicated Search panel only if it provides extra value).
- “Remove hardcoded defaults for credentials” (OAuth is the default; legacy env-based handle/app-password is not required).
Next (prioritized)
Panels roadmap (canonical)
This section was previously maintained in packages/concretesky/js/panels/TODO.md.
It has been merged into this file so there is a single source of truth.
Planned features
- Panel API docs: keep
API.mdcurrent as new hooks are added. - Standardize stable keys: all list-like components should use
data-kfor scroll anchoring. - Windowing (virtualized lists) for long lists to reduce DOM size. (true DOM windowing implemented via
ListWindowingControlleracross notifications, connections, followers/following, feed, notification bar, and my posts) - Better “exhausted” UX for infinite scroll (explicit end-of-feed states per panel). (implemented via
renderListEndcap()and adopted across notifications, connections, followers/following, feed, notification bar, and my posts) - Centralized error/toast reporting for load failures (common hook).
- Persist per-panel scroll position across tab switches (opt-in).
This is the roadmap for improving the ConcreteSky panel system and adding upcoming UI features (notifications, commenting, threaded rendering, and a content/details panel).
Goals
- Keep the SPA within 100% viewport width at all times (no horizontal overflow).
- Panels should reduce columns as space shrinks, down to a single primary column.
- On mobile, users should swipe horizontally between panels.
- Panels should be modular: shared layout, shared UX APIs, pluggable menus/controls.
- Support adding new panels later by dropping in a template module.
Panel System Stabilization (Do First)
1) Make sizing rules consistent and predictable
- Ensure a single source of truth for:
- card width:
--bsky-card-w - card gap:
--bsky-card-gap - panel density:
--bsky-panel-pad(-dense)etc
- card width:
- Note: theme preferences now support a “comfortable” density mode by overriding the
--bsky-panel-*-densetokens. - Verify panel sizing logic aligns with the new
<bsky-panel-shell dense>(avoid “extra padding models” drifting).
2) Mobile behavior (no overflow)
- Mobile mode is “one panel per screen”:
.panel { flex: 0 0 100%; min-width: 100%; }- horizontal scroll +
scroll-snap
- Ensure stored panel widths never apply on mobile.
3) Reduce “layout regression” risk
- Add a small debug helper (optional) to log:
- panelsWrap width
- each panel’s flex-basis + measured width
- card width/gap
- Add a single “reset layout” escape hatch (already exists) and confirm it also resets any new panel keys.
Panel Templates / Registry (Foundation)
4) Standardize panel templates
- Templates live in:
packages/concretesky/js/panels/templates/ - Each template exports:
name(must matchdata-tab/data-panel)title(tab label)mountHtml(rendered when active)- optional
defaultActive
Example template:
// packages/concretesky/js/panels/templates/notifications.js
export default {
name: 'notifications',
title: 'Notifications',
mountHtml: '<bsky-notifications></bsky-notifications>',
defaultActive: false,
};
5) Standardize panel utilities
- Shared utilities live in:
packages/concretesky/js/panels/panel_api.js - Keep it framework-free; provide small helpers panels can share:
registerPanelTemplate()/getPanelTemplates()getDefaultActiveTabs()debounce(fn, ms)bindNearBottom(scroller, cb, { threshold, enabled })isMobilePanelsViewport()
Example usage:
// inside a panel that uses <bsky-panel-shell>
import { bindNearBottom } from '../panels/panel_api.js';
const scroller = this.shadowRoot.querySelector('bsky-panel-shell')?.getScroller?.();
this._unbindNearBottom?.();
this._unbindNearBottom = bindNearBottom(scroller, () => this.load(false), {
threshold: 220,
enabled: () => !this.loading && this.hasMore,
});
Notifications Panel (High Priority)
6) Add a Notifications panel template
- Add:
packages/concretesky/js/panels/templates/notifications.js - Register in
panel_api.js
Code:
// packages/concretesky/js/panels/panel_api.js
import notifications from './templates/notifications.js';
registerPanelTemplate(notifications);
7) Implement <bsky-notifications> component
- File:
packages/concretesky/js/components/notifications.js(already exists but needs “panel shell parity” and UX). - Requirements:
- Display grouped/filtered notifications (mentions, replies, likes, reposts, follows).
- Mark as read/unread.
- Click notification -> open associated post/thread in content panel.
- Pagination + infinite scroll.
- Optional polling interval or manual refresh.
Skeleton:
// packages/concretesky/js/components/notifications.js
import { call } from '../api.js';
import { bindNearBottom, debounce } from '../panels/panel_api.js';
class BskyNotifications extends HTMLElement {
constructor(){
super();
this.attachShadow({ mode: 'open' });
this.items = [];
this.loading = false;
this.cursor = null;
this.hasMore = false;
this.filters = { q: '', types: new Set(['all']) };
this._unbindNearBottom = null;
}
connectedCallback(){
this.render();
this.load(true);
}
async load(reset){
// TODO: implement via ConcreteSky API (cache + network modes as needed)
// const out = await call('cacheQueryNotifications', { ... })
}
openItem(item){
const uri = item?.uri;
if (!uri) return;
this.dispatchEvent(new CustomEvent('bsky-open-content', {
detail: { kind: 'post', uri },
bubbles: true,
composed: true,
}));
}
render(){
this.shadowRoot.innerHTML = `
<bsky-panel-shell title="Notifications" dense>
<div slot="toolbar">TODO filters/search</div>
<div>TODO list</div>
</bsky-panel-shell>
`;
const scroller = this.shadowRoot.querySelector('bsky-panel-shell')?.getScroller?.();
this._unbindNearBottom?.();
this._unbindNearBottom = bindNearBottom(scroller, () => this.load(false), {
enabled: () => !this.loading && this.hasMore,
});
}
}
customElements.define('bsky-notifications', BskyNotifications);
Acceptance criteria:
- Loads fast from cache/DB.
- Does not reflow panels unexpectedly.
- Works as a single swipe panel on mobile.
Content Panel (Post Details) (High Priority)
8) Add a “content” panel template
- Template:
packages/concretesky/js/panels/templates/content.js - Mounted component:
<bsky-content-panel>(new).
Template:
// packages/concretesky/js/panels/templates/content.js
export default {
name: 'content',
title: 'Content',
mountHtml: '<bsky-content-panel></bsky-content-panel>',
defaultActive: false,
};
9) Behavior: click a post -> open content panel
- In Posts panel:
- Clicking a post (excluding links/buttons) selects it.
- Emits an event (suggested):
bsky-open-content- detail:
{ uri, cid, kind: 'post' }
- detail:
- In App shell / panels controller:
- If the content panel is not visible, activate it.
- Ensure layout makes room by reducing the posts panel by exactly 1 column when possible.
Posts click emission (pattern):
// inside bsky-my-posts
onClickPost(uri, cid){
this.dispatchEvent(new CustomEvent('bsky-open-content', {
detail: { kind: 'post', uri, cid },
bubbles: true,
composed: true,
}));
}
App-side handler (suggested location: inside bsky-app after bootTabs):
// inside bsky-app connectedCallback()
this.shadowRoot.addEventListener('bsky-open-content', (e) => {
const { uri, cid } = e?.detail || {};
if (!uri && !cid) return;
// 1) make content panel visible
// 2) pass selected uri/cid to <bsky-content-panel>
// 3) reduce posts panel by exactly 1 column if possible
});
Content panel skeleton:
// packages/concretesky/js/components/content_panel.js
import { call } from '../api.js';
class BskyContentPanel extends HTMLElement {
constructor(){
super();
this.attachShadow({ mode: 'open' });
this.selection = null; // { uri, cid }
}
setSelection(sel){
this.selection = sel;
this.load();
}
async load(){
// TODO: fetch post + thread + likes/reposts/replies
// const out = await call('cacheGetPostDetails', { uri: this.selection.uri })
this.render();
}
render(){
this.shadowRoot.innerHTML = `
<bsky-panel-shell title="Content" dense>
<div slot="toolbar">TODO actions (reply/like/repost)</div>
<div>TODO post details + thread tree</div>
</bsky-panel-shell>
`;
}
}
customElements.define('bsky-content-panel', BskyContentPanel);
Acceptance criteria:
- Content panel is exactly 1 column wide (card width based).
- Posts panel loses exactly 1 column, not more.
- Closing the content panel restores the previous layout.
- On mobile: content panel becomes a swipeable panel.
Commenting + Interactions (High Priority)
10) Add a comment composer web component
- New component:
<bsky-comment-composer> - Requirements:
- Reply-to context
- character count
- submit + disabled states
- emits event on successful post/reply
Skeleton:
// packages/concretesky/js/components/comment_composer.js
import { call } from '../api.js';
class BskyCommentComposer extends HTMLElement {
constructor(){
super();
this.attachShadow({ mode: 'open' });
this.replyTo = null; // { uri }
this.text = '';
this.loading = false;
}
setReplyTo(uri){
this.replyTo = uri ? { uri } : null;
this.render();
}
async submit(){
if (this.loading) return;
const text = String(this.text || '').trim();
if (!text) return;
this.loading = true;
this.render();
try {
// TODO: wire to create-post / create-reply endpoint
// const out = await call('createReply', { parentUri: this.replyTo?.uri, text })
this.text = '';
this.dispatchEvent(new CustomEvent('bsky-post-created', { bubbles: true, composed: true }));
} finally {
this.loading = false;
this.render();
}
}
render(){
this.shadowRoot.innerHTML = `
<style>
:host{display:block}
textarea{width:100%;min-height:90px;background:#0b0b0b;color:#fff;border:1px solid #333;border-radius:10px;padding:8px}
.row{display:flex;justify-content:space-between;align-items:center;margin-top:6px;gap:8px}
button{background:#111;border:1px solid #555;color:#fff;padding:8px 10px;border-radius:10px;cursor:pointer}
button:disabled{opacity:.6;cursor:not-allowed}
.muted{color:#aaa}
</style>
<textarea placeholder="Write a reply…">${this.text || ''}</textarea>
<div class="row">
<div class="muted">${(this.text || '').length}/300</div>
<button ${this.loading ? 'disabled' : ''}>${this.replyTo ? 'Reply' : 'Post'}</button>
</div>
`;
const ta = this.shadowRoot.querySelector('textarea');
ta?.addEventListener('input', () => { this.text = ta.value; });
const btn = this.shadowRoot.querySelector('button');
btn?.addEventListener('click', () => this.submit());
}
}
customElements.define('bsky-comment-composer', BskyCommentComposer);
11) Implement post details interactions
- In content panel:
- show likes
- show reposts
- show replies
- actions: like/repost/reply (as permitted)
Thread/Comment Tree Rendering (High Priority)
12) Add a thread tree renderer
- New component:
<bsky-thread-tree> - Requirements:
- Render replies in a tree (parent -> children).
- Collapse/expand branches.
- Lazy-load deeper replies if needed.
- Visual connectors and indentation.
Skeleton:
// packages/concretesky/js/components/thread_tree.js
class BskyThreadTree extends HTMLElement {
constructor(){
super();
this.attachShadow({ mode: 'open' });
this.root = null; // tree node
this.collapsed = new Set();
}
setThreadTree(rootNode){
this.root = rootNode;
this.render();
}
toggle(uri){
if (!uri) return;
if (this.collapsed.has(uri)) this.collapsed.delete(uri);
else this.collapsed.add(uri);
this.render();
}
renderNode(node, depth){
const kids = Array.isArray(node?.replies) ? node.replies : [];
const isCollapsed = this.collapsed.has(node?.uri);
return `
<div class="node" style="--d:${depth}">
<div class="card">
<div class="meta">${node?.author?.handle ? '@' + node.author.handle : ''}</div>
<div class="text">${node?.text || ''}</div>
</div>
${(!isCollapsed && kids.length)
? `<div class="kids">${kids.map((k) => this.renderNode(k, depth + 1)).join('')}</div>`
: ''
}
</div>
`;
}
render(){
this.shadowRoot.innerHTML = `
<style>
:host{display:block}
.node{margin-left:calc(var(--d) * 16px)}
.card{border:1px solid #333;border-radius:10px;padding:8px;background:#0b0b0b;margin:6px 0}
.meta{color:#aaa;font-size:.9rem}
.text{white-space:pre-wrap}
</style>
${this.root ? this.renderNode(this.root, 0) : '<div class="muted">No thread.</div>'}
`;
}
}
customElements.define('bsky-thread-tree', BskyThreadTree);
Acceptance criteria:
- Stable ordering within a thread.
- Does not cause panel overflow.
Backend/API Tasks (Needed for the above)
13) Add endpoints for post details + thread data
- Add/extend API calls for:
- post details by URI
- thread fetch by URI
- likes list
- reposts list
- replies list
- create reply
14) Cache strategy
- Cache post details + thread responses to reduce repeated network fetch.
- Consider background hydration (similar to profile hydration).
UX/Polish
15) Keyboard + accessibility
- Focus management when opening content panel.
- Proper
role="region"and labels.
16) Performance
- Window long lists where needed.
- Avoid re-sorting loaded items; preserve stable ordering unless explicitly requested.
Suggested Implementation Order
- Stabilize panel sizing (no overflow, reliable mobile swipe).
- Add Content panel (template + component + open/close flow).
- Upgrade Notifications panel to use content panel for deep links.
- Add thread tree rendering inside content panel.
- Add comment composer and reply flow.
- Iterate on caching + performance.
1) Backfill clarity + progress
- Deep notifications/posts backfill status: retention caveats + current cursor/coverage + “done” signal. (Implemented:
cacheStatus.backfill+ DB Manager display.) - Better progress stats for notification backfill (inserted/updated/skipped per chunk) + surfaced in UI. (Implemented.)
- Long-running backfills safer: explicit cancel/stop controls + clearer messaging. (Implemented: DB Manager stop + HUD cancel hooks; copy tweaks optional.)
2) Cache lifecycle + maintenance
- Add cache pruning strategy (retention / TTL + vacuum) to keep
cache.sqlitebounded. (Implemented: admin actions + scheduled jobconcretesky_cache_maintenance.) - Add an admin-only “DB inspector” view (table sizes, indexes, schema version, last vacuum, writable-path checks). (Implemented:
cacheDbInspect+ DB Manager.) - Add cache schema migration smoke checks (at least a minimal command/script to run migrate + verify schema version). (Implemented: CLI
concretesky:cache:migrate-check.)
3) Security / access control
- Add configurable access-control guard for the SPA (admin/whitelist/group-based). (Implemented:
CONCRETESKY_UI_*+ server-side enforcement.) - Keep OAuth endpoints workable:
/oauth/client_metadataand/oauth/callbackmust remain reachable. - Document and harden automation mode defaults (JWT allowlist, superuser requirement, when to enable
CONCRETESKY_JWT_ENFORCE). (Implemented: README guidance + safe defaults.)
4) UX / configuration
- Persist DB Manager settings (calendar selections, pagesMax, notifications window, posts filter) optionally in localStorage. (Implemented:
bsky.dbManager.prefs.) - Add staleness threshold configuration (what counts as “stale” for cached rows and UI warnings). (Implemented in DB Manager; can be propagated to other panels if desired.)
- Improve rate-limit errors (surface retry-after / backoff hints; reduce spammy retries). (Implemented: Retry-After surfaced + UI backoff in long-running loops.)
- Add a compact, themeable style system (CSS variables) to keep colors/spacing consistent across panels. (Implemented: theme tokens in
bsky-panel-shell; Groups UI uses tokens.) - Add a Theme dashboard screen (ConcreteCMS Dashboard) to adjust Dark/Light presets and override
--bsky-*CSS variables (with color pickers), stored server-side and injected into the SPA. (Implemented:/dashboard/system/concretesky/theme+concretesky.theme_jsoninjection.)
5) Notifications UX (bar + panel)
- Enrich notification entries: show who performed the action (identity block/avatar/handle), and show relationship state (following / follows-you / mutual) where relevant.
- Add quick actions/links (open profile, open post, follow/unfollow when appropriate) and improve grouping/layout readability.
6) Data features
- Optional FTS search for cached profiles/posts (SQLite FTS5), with a clean fallback to LIKE when unavailable. (Posts FTS implemented; falls back to LIKE if FTS5 isn't available.)
- Export tools (CSV/JSON) for cached datasets (followers/following/notifications/posts) for AI workflows. (Posts/notifications/followers/following export implemented via
cacheExport.) - Add “signals” / analyst views for suspicious or unusual activity patterns (e.g. account age buckets, follower/following ratio, sudden spikes, top interactors).
7) Posts / Threads UI (feed + content)
- Posts feed should be thread-centric:
- Default: compact “summary cards” for posts/replies/reposts.
- Expand/collapse (+/−) per entry to show the full thread inline (no accidental cross-thread nesting).
- Clicking any entry opens the Content panel in “conversation/thread view”.
- Fix incorrect nesting in Posts feed where unrelated items appear under the same thread (treat feed items as independent unless explicitly expanded to a fetched thread).
- Inline media in-client (no forced redirect to bsky.app):
- Images: render grids/preview in cards and in thread view.
- Bluesky video: render/play inline where possible; add fallback behavior for HLS if the browser can’t play it.
- Commenting / replying:
- Dedicated comment UI web components under
packages/concretesky/js/comment/*. - Reply composer usable in both the Posts panel (inline in the card) and the Content panel (thread view).
- Support full Bluesky media options for replies (at minimum: multi-image upload + alt text; extend to video as supported).
- After posting, refresh the thread/feed to reflect the new reply.
- Dedicated comment UI web components under
- Reposting:
- Repost/unrepost actions in cards and thread view.
- Keep UI state in sync (optimistic update + refresh).
9) Bluesky feature parity (expansive)
Composer / Posting
- Bluesky-like composer lightbox (Posts panel).
- 300-char counter + over-limit enforcement.
- Thread composer "+" (multi-part posts) with sequential root/parent chaining.
- Emoji picker.
- GIF link insertion (URL into text).
- Multi-image attachments (up to 4) + alt text.
- Language tagging on posts via
record.langs(uses browser languages). - Drafts: local drafts per context + autosave (text + interaction settings; incl. thread parts).
- Drafts: persist media selections across reloads (IndexedDB-backed; text/settings remain in localStorage).
- Scheduled posts (client-side scheduler + server job to publish).
- Edit post (Bluesky supports record update semantics; confirm UX + history).
- Post deletion UX improvements (undo window, optimistic removal).
- Post “translations” / inline translate UX (if supported; else defer) + copy-to-clipboard.
- Per-post audience controls parity (where applicable): visibility-like semantics (note: ATProto is public-first; document constraints clearly).
- Drafts: autosave to IndexedDB (text + media + gates) for crash-proof drafts. (IndexedDB snapshot + recovery path.)
- Drafts: export/import (JSON) for power users. (Composer settings: Export/Import.)
Facets (mentions, links, hashtags)
- Auto-link URLs into facets (
app.bsky.richtext.facet#link). - @mention parsing + DID resolution into facets (
#mention). - Hashtag parsing into facets (
#tag). - Unicode-safe facet indexing (UTF-8 byte indices).
Interaction settings (gates)
- Reply controls (threadgate) for new root posts:
- Everyone (no gate)
- Nobody (
allow: []) - Custom: mentioned users / followers / following / list
- List picker support for reply gates (loads lists via
app.bsky.graph.getLists, fallback to paste AT-URI). - Quote/embedding control (postgate): disable embedding/quotes.
- Expose additional postgate rules if/when supported (future lexicon expansion).
- UI copy parity (Bluesky wording + per-setting explanations).
Media
- Images upload + embed.images.
- Video upload + embed.video support (upload flow + transcoding constraints).
- Media processing UX: progress bar, cancel, retry.
- Content accessibility: alt-text required toggle + warnings when missing.
Embeds
- Render quoted posts (record embeds) in feed/thread view.
- Render external link cards where available.
- Better embed fallback: fetch external cards when only a URL is present.
- Record-with-media embeds (quote + images/video) full parity.
- Embed accessibility parity: ensure quoted/external cards expose meaningful text for screen readers.
- Embed safety: guard against layout shifts (image aspect reservation) and malicious URLs.
Threading / Conversation UX
- True nested replies across all surfaces.
- Hide-root behavior where duplicate root cards would appear.
- Inline reply composer inserted under clicked post; second click closes.
- Better “reply-to” UX like Bluesky: show the parent snippet above composer.
- “Detach thread” / “Continue thread” UX for composing follow-ups.
- Thread actions parity: collapse/expand all, copy link, share, open in new panel. (implemented in
<bsky-thread-tree>) - Conversation context parity: show “you replied” / “you reposted” indicators.
Content labels / Safety
- Self-labeling UI (e.g. “Adult content”) and attach labels per post.
- Content warnings UI (toggle + reason) and render in timeline.
- Respect viewer moderation preferences (hide/blur sensitive media).
- Report post/account flows (if supported via endpoints).
- “Mute thread” / “hide post” local actions (site-local) if network endpoints don’t exist.
- Safety UX parity: clear per-post label explanations + “Why am I seeing this?” affordance.
Lists / Feeds
- Browse + manage lists (create, delete, add/remove members).
- Timeline view for a list feed.
- Custom feed browsing (app.bsky.feed.getActorFeeds / getFeedSkeleton).
- Feed pin/reorder persistence (per account) + export/import.
- Curate feeds in UI: add/remove to home, rename aliases, icons.
Notifications + Activity
- Full parity notification rendering (reason strings, grouped actions).
- Inline actions in notifications (like/repost/follow) without context switch.
- Background polling with backoff + “unread” badge counts.
- Mark-as-read semantics parity (per-notification + “mark all read”) with server sync where possible.
- Notification preferences UI (what to notify on; per-account).
- Activity filters: mentions, replies, follows, likes, reposts, quotes.
- “Activity” view parity (likes/reposts on your posts; your own actions timeline).
Discoverability / Search
- Global search parity (people + posts + feeds) with a unified results UI.
- Hashtag browsing (tap a hashtag → feed view).
- Saved searches / pinned queries (localStorage + optional SQLite persistence).
- Trending view parity (if available; else approximate via cached aggregation: top hashtags/links).
- Starter packs (if supported): browse + view members + follow-all with rate limiting.
- User lists discovery: show lists that include an actor (if supported) and list search.
Profiles / Identity
- Profile edit UI (display name, bio, avatar/banner upload if supported).
- “Followers you know” / mutuals UX parity.
- Relationship badges everywhere (following/follows-you/mutual) with fast cached lookups.
- Profile tabs parity: posts, replies, media, likes (if supported via endpoints) + counts.
- Profile moderation controls: mute/block/report from profile with clear state.
- Account switching UX parity: multi-account picker + per-account state isolation.
Bookmarks / Saves
- Bookmarks / saved posts (if supported; else implement local-only saved list in SQLite per actor).
- “Read later” list UI + export.
- Local-only tagging for saves (labels/folders), plus quick filters.
Media parity
- Video upload +
embed.videosupport (upload flow + constraints + playback). - GIF picker parity (provider abstraction; allow swapping sources).
- Media processing UX: progress, cancel, retry.
DMs / Chat (if/when supported)
- DM surface (inbox + thread view) if Bluesky exposes stable endpoints.
- If not supported: explicitly defer and keep UI hooks modular.
Blocks / Mutes / Moderation prefs
- Mute/unmute user UI + fast cached state.
- Block/unblock user UI + fast cached state.
- Muted words/phrases UI (if supported), including per-language and per-scope toggles.
- List-based moderation parity (mute list / block list) if supported.
- Labelers management UI (subscribe/unsubscribe labelers; explain implications) if supported.
Feeds / Timeline UX
- Feed picker parity: Following + custom feeds with pin/reorder.
- Feed composition preferences (hide replies, show reposts, etc.) if supported.
- Better infinite-scroll UX: see “Panels roadmap (canonical)” (windowing/virtualized lists, scroll position restore, exhausted states, “new posts” banner).
Settings / Preferences
- Viewer preferences UI (hide/blur sensitive media, labelers, moderation prefs).
- Per-account settings persistence (SQLite + UI sync).
- Session diagnostics UI: show token expiry, DPoP status, last refresh, last error.
- Privacy/security settings: clear cached tokens locally, clear per-account caches, “forget account” UX.
- Theme preferences: compact/comfortable density, font size, reduced motion. (Implemented via Theme dashboard prefs; reduced motion currently targets smooth scrolling + drag animation + spinners; remaining: audit any rAF-driven effects/transitions.)
Account lifecycle
- Handle change support (detect + update cached profile links).
- DID rotation / recovery story (document constraints; handle gracefully if it occurs).
- “Sign out everywhere”/token revoke UX (if supported) or local-only sign-out.
- Account migration tooling for site-local data (saved posts, group membership): export/import keyed by DID.
Diagnostics / Developer tooling
- Built-in “Request Log” panel (admin-only): last N XRPC calls, status, latency, rate-limit headers.
- “Replay last request” button (safe GETs only) and “copy curl” for debugging.
- Feature flags UI (localStorage + optional server-config): enable experimental panels/features.
- Minimal error reporting hook (optional): log to a site-local table for support.
Internationalization
- i18n strategy for Web Components (string tables, locale selection, fallbacks).
- Date/time formatting parity (relative times, locale-sensitive).
- RTL support audit for panel layouts.
Offline-ish / resilience
- Background refresh scheduler (jobs) for posts + notifications + profiles with backoff.
- Conflict-safe refresh (avoid duplicates; stable ordering; cursor correctness).
- “Low-connectivity mode” toggle: fewer images, smaller page sizes, less aggressive polling.
- Robust retry UX: per-surface “retry now” buttons + explain backoff state.
Accessibility / Polish
- Keyboard navigation for composer parts + pickers.
- Proper focus management when opening/closing dialogs.
- Screenreader labels for all icon buttons.
8) People monitoring (watchlist + activity)
- Add a “Watchlist” / People Monitoring panel:
- Add/remove Bluesky users by handle/DID.
- Persist watchlist in SQLite (and expose via API).
- Cache watched users’ profile + recent posts on-demand and on a schedule.
- Activity stats for watched users:
- Posting frequency, active hours/days, recent streaks.
- Interaction stats: replies/likes/reposts involving you and/or other watched users.
- “New since last check” summaries.
- Notifications for watched users:
- In-app notifications when they post (and optionally when they are replied to / quoted / reposted).
- Optional: email notifications and web push notifications (future).
Maintenance
- Consider deprecating or clearly labeling the legacy app-password login path (
authLogin) now that OAuth is the default.
Facebook Groups parity (Bluesky-first)
This section is the backlog for building “groups” as a first-class product. Some features map directly to ATProto primitives; others will be implemented as site-local group metadata + moderation while posting public content to Bluesky.
1) Group model (foundation)
-
Define a durable Group model:
- SQLite schema + migrations (groups + membership + audit log).
- Export/backup + import (site portability).
- Clarify constraints and invariants (owner transfer, delete semantics).
-
Group-scoped routing in UI:
- Group selector (site-level nav) + persisted active group.
- Shell stays in sync on group changes (live refresh).
- Group home panel with MVP activity feed.
-
Group settings UI (admins only).
-
MVP server API for site-local groups (list/get/create/update/join/leave).
-
MVP Groups panel (list + join/leave + admin create).
2) Membership + roles
- Membership states:
member|pending|blocked|invited. - Roles:
admin|moderator|member(+ optionaltrusted). - Join flow parity:
- Join questions (form builder + stored answers).
- Approval queue for closed groups (admin-only MVP).
- Invite links (rotatable tokens) (admin-only MVP).
- Group rules (markdown) + onboarding gate (“must accept rules”) (MVP).
3) Group feed + posting
- Group feed panel:
- MVP: tag-based feed from Bluesky search.
- Sort modes (new, top, hot) with clear semantics.
- Topic tags (group-local taxonomy).
- Posting into a group:
- Post composer supports “post to group” (adds group tag).
- Group-only posting rules (MVP): members can submit; closed/secret require approval; public posts publish immediately.
- Slow-mode / per-member rate limits (MVP: per-group cooldown seconds).
- Announcement posts + pinning:
- Pin up to N posts; show pinned section.
- Admin-only announcement marker.
4) Moderation queue (Facebook-style)
- Moderation surfaces:
- Pending posts queue (approve/deny) (MVP: mods/admins only).
- Report queue (user reports with reasons) (MVP).
- Member requests queue (approve/deny).
- Moderator actions:
- Remove post (from group feed) (MVP: site-local hide/unhide), keep audit log.
- Warn/suspend/ban member (group-local enforcement).
- Keyword filters / blocked phrases (MVP: deny / require approval).
- Audit log UI + export (who did what, when).
5) Units / Guides (learning content)
- Units feature parity (Facebook “Guides”):
- Units have title/description + ordered steps.
- Steps can link to posts, external URLs, or local HTML blocks.
- Completion tracking per member.
6) Events
- Group events:
- Create/edit event, RSVP list.
- Optional reminders (email/push later).
- Event posts in group feed.
7) Files / Media library
- Group files (site-local storage): upload, organize, permissions.
- Link media to posts/units/events.
- Anti-abuse constraints (size limits, types, virus scan hook).
8) Insights / analytics
- Group insights dashboard:
- Growth (members over time, join sources).
- Engagement (posts/comments/reactions per day).
- Top contributors, top posts.
- Moderation workload stats.
9) Discovery + cross-site portability
- “Directory” view of groups on a site (categories, search).
- Export/import group configuration + membership list.
- Non-Concrete host strategy:
- Extract core PHP library (auth + XRPC + SQLite + jobs runner) so other PHP CMSs can integrate.
- Define a minimal cron entrypoint equivalent to Concrete jobs.
10) Post types + engagement parity
- Poll posts (group-local): create, vote, results visibility rules.
- Q&A / “Ask a question” post type (accepted answer + surfaced state).
- Anonymous posting (admin-controlled; audited; optional moderator reveal).
- Post reactions beyond likes (group-local) if desired; ensure anti-abuse constraints.
11) Group chat / real-time community
- Group chat surface (site-local): channels, member mentions, moderation.
- If Bluesky DMs become viable: optionally bridge group chat to DMs for small groups.
12) Admin automation (“Moderation Assist”)
- Auto-approve rules (account age, mutuals, prior member standing, answered questions).
- Auto-decline heuristics (link spam patterns, repeated phrases, suspicious domains).
- Scheduled moderation reports (daily digest) for admins.
13) Membership + community management
- Member badges / roles expansion (e.g. “Top contributor”, “New member”, “Verified”).
- Welcome posts + onboarding checklist.
- Member directory view with filters (new members, most active, moderators).
14) Content organization + knowledge base
- Topics taxonomy tooling: merge/rename, pin topics, topic rules.
- Wiki/Docs (site-local): pages with history + editor permissions.
- Group “Files” + “Docs” unified library (search, tagging).
15) Integrations + sharing
- Share group content externally:
- Generate canonical permalinks that work outside the panel UI.
- Optional OpenGraph cards for posts/events/units.
- Webhook/event stream for group actions (new member, post approved, report created).
16) Safety + compliance
- Privacy controls parity: secret groups should not leak membership/content in listings.
- Data retention tooling: delete member data, export member data, purge audit logs by policy.
- Abuse handling: rate limits per member, post cooldowns, link limits, attachment limits.
17) Governance + rules enforcement
- Appeals workflow: member can appeal a denial/ban; moderator review; audit trail.
- Moderator permissions granularity (who can ban vs approve vs edit rules vs manage files).
- “Admin announcements” system: schedule announcements, pin duration, expiry.
- Rule change history + member re-acknowledgement gate when rules update.
18) Subgroups + multi-space communities
- Subgroups / channels inside a group (topic-based rooms) with per-channel posting rules.
- Cross-posting between subgroups with attribution.
- “Collections” for organizing posts (saved threads inside a group).
19) Monetization + fundraising (optional)
- Paid groups / memberships (site-local billing integration; keep optional and decoupled).
- Donations/fundraisers: campaign posts + progress display + donor privacy controls.
- Member perks (badges, role unlocks) managed by admins.
20) Notifications + digests (group-scoped)
- Group notification settings: new posts, mentions, announcements, events, units.
- Email digests (daily/weekly) for group activity (site-local mailer integration).
- Web push notifications (later): subscribe per group + per device.
21) Scheduling + content calendar
- Scheduled posts (group-local queue) + approval + publish windows.
- Content calendar view (admin/mod): upcoming posts/events/announcements.
- Recurring events + automated reminder posts.
22) Reputation + trust signals
- Trust score (site-local): tenure, approvals, reports, engagement.
- Anti-spam gating: require phone/email verification (site-local) if desired.
- “Slow mode” auto-enable when spam spikes (rules engine).
23) Accessibility + inclusivity (group features)
- Alt-text enforcement for group-required media.
- Caption uploads for video (site-local) + transcript fields for events/units.
- Accessibility audit: keyboard nav for mod tools and forms.
What's inside
1 north star, 2 parity targets, 8 done items, 2 superseded, 16 planned sections with code skeletons and acceptance criteria
Change this for your project
- Replace
packages/concretesky/with your own package path - Replace
concretesky_cache_maintenancewith your own job name - Replace
CONCRETESKY_UI_*andCONCRETESKY_JWT_ENFORCEwith your own config keys - Replace
bsky-*custom element prefixes with your own component namespace
Where it goes
Load as policy context for the agent, or keep beside the code enforcing the rules.
Worth borrowing
- Using a single shared controller per behavior to keep panels thin
- Storing panel templates in a registry with name, title, and mountHtml exports
- Implementing cache-first UX with SQLite and fallback to network for fast browsing
Related Documents
DeepSeek R1: Case Study in Failed Extrinsic Alignment
Compiles public security research and independent findings to argue that extrinsic alignment methods are insufficient for AI safety, using DeepSeek R1 as a case study.
Guardrails, Safety & Content Filtering
Implements a layered guardrail system with input validation, output validation, and content filtering to protect LLM applications from prompt injection, jailbreaks, and data leaks.
My Hyprland Rice Setup - Context for Claude Code
Documents a Hyprland rice setup with system specs, aesthetic preferences, workflow safety practices, and a list of current issues to solve.
AI Safety & Guardrails for Voice Assistants
Defines a multi-layer safety architecture for voice assistants, covering input filtering, deterministic FAQ routing, RAG-grounded AI responses, and output guardrails.