This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/ROADMAP.md
Jeffrey Smith 768f15b3cd
All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-frontend (push) Successful in 5s
CI/CD / test-go-pg (push) Successful in 2m35s
CI/CD / test-sqlite (push) Successful in 2m52s
CI/CD / build-and-deploy (push) Successful in 30s
Feat v0.5.5 upgrade testing (#35)
Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
2026-03-30 20:50:53 +00:00

43 KiB
Raw Blame History

Switchboard Core — Roadmap

Current: v0.5.3 — Chat Polish + Integration Testing

Fork of chat-switchboard, gutted to a pure extension platform. All AI/chat features removed from the kernel. What remains is the minimum viable platform that extensions build on.

Retained kernel capabilities

  • Auth: builtin (simple), mTLS, OIDC
  • Identity: users, teams, groups, permissions (RBAC)
  • Packages: surfaces, extensions, libraries, workflows
  • Starlark sandbox: capability-gated modules
  • Storage: object storage (PVC, S3), ext_data tables
  • Realtime: WebSocket hub, presence, multi-replica HA
  • Ops: audit log, notifications, maintenance goroutine

Phase 0 (complete)

Step Status Description
1. Module rename chat-switchboardswitchboard-core
2. Delete packages 15 Go packages, 29 handler files removed
3. Gut stores/models 40 → 20 store interfaces, kernel-only models
4. Fresh migrations 9 files × 2 dialects, 27 tables
5. Fix compilation go build ./... clean, 300+ stale route lines cut
6. Fix tests 8 test packages pass, ~12K stale test lines pruned
7. Frontend gut Shell + SDK only, 50+ files of chat/notes/projects code removed
8. New ICD Full OpenAPI 3.0.3 spec — 160 operations across 22 tag groups
9. CI/CD + Dockerfile Single unified image, FE/BE split removed, DB names updated, k8s var alignment fixes (resource quantities, image name, rollout deployment name)
10. Smoke test K8s deploy live at switchboard.gobha.ai/test, nginx BASE_PATH fixed, login→admin flow verified, branding updated

v0.2.x — SDK & Triggers

The contract that extensions build against. Three trigger primitives, SDK stabilization, and the first rebuilt extension (tasks).

v0.2.0 — RBAC + Settings Cascade (complete)

Step Status Description
Admin → RBAC group surface.admin.access permission + Admins system group replaces role == "admin" checks. Admin bypass removed from permission middleware.
Settings cascade user_overridable flag, three-tier resolution (global → team → user), team settings API
Settings override model Shipped with settings cascade above

v0.2.1 — Default Surface + ICD

Step Status Description
Default surface routing / redirects to configurable default surface. No surfaces → admin. First install becomes default. Changeable in admin settings.
ICD (API contract) Full OpenAPI 3.0.3 spec — 160 operations, 22 tag groups, reusable component schemas. Served at /api/docs.

v0.2.2 — Event Bus + Triggers

Step Status Description
Event bus subscriptions Extensions register event patterns in manifest. Wired via bus.Subscribe() on startup. Async handler invocation.
Webhook triggers Inbound HTTP at /api/v1/hooks/:package_id/:slug. HMAC-SHA256 verification. Synchronous Starlark handler response.
Scheduled tasks User-created cron tasks with restricted sandbox (no raw HTTP, no DB table creation). Runs as creator identity. Templates from extensions. Dedicated schedules API.
Trigger admin API CRUD for triggers + schedules. Enable/disable, execution logs, per-package listing.

v0.2.3 — SDK + Task Extension

Step Status Description
SDK stabilization sw.api.ext(), sw.storage, sw.theme.tokens, sw.ui, sw.slots, sw.actions — six new SDK modules for extension development
Task extension Full task surface rebuilt as Starlark extension: CRUD API, kanban/list views, event triggers, webhook integration, notifications on completion

v0.2.4 — Shell Navigation + Schedules

Step Status Description
SDK Topbar sw.shell.Topbar — composable navigation bar (title + extension slot + bell + user menu). Surfaces get consistent nav for free.
Schedules surface New packages/schedules/ wrapping kernel cron API. Table view, cron preview, enable/disable, manual run, execution logs.
Manifest icons icon field in manifest.json (emoji). Surfaces API returns icon. UserMenu renders per-surface icons.
UserMenu cleanup Removed dead Chat/Notes/Projects links. Menu driven by surfaces API. Core surfaces filtered.
isAdmin RBAC fix can.js isAdmin() now checks surface.admin.access grant instead of deprecated role column.

v0.2.5 — UI Polish + Dead Code Audit

Step Status Description
UI bug pass Reviewed admin, settings, login, welcome surfaces in light/dark themes. Fixed settings crash (models API undefined). Removed dead chat settings from both user and admin settings.
Dead code sweep Removed ~700 lines: orphaned CSS, dead Go types, stale event code, unused test helpers, dead settings UI (chat defaults, system prompt, default model, policies, web search, compaction, memory).
Template cleanup Removed stale CSS link tags and orphaned chat-pane.html. Updated doc comments throughout.
Package proof-of-concept status Created README.md for tasks + schedules with graduation criteria. Added missing manifest icons.
Welcome surface New fallback surface when no extensions installed. Topbar + welcome card with admin link. Replaces /admin as final redirect target.
Default surface routing Resolution chain: user preference → global config → first extension → /welcome. Users can set personal default in Settings > General. Admin sets global default in Admin > Settings.
Admin navigation Replaced Back button with UserMenu in admin topbar. Eliminates back-button infinite loop.

v0.2.6 — Admin Settings Audit

Step Status Description
Admin settings E2E All 7 admin settings sections verified (default surface, registration, banner, message bar, footer, vault, email). Dead code removed: sectionCategory() pruned of AI/routing/channel vestiges, PublicSettings() stripped of chat-era fields (system_prompt, retention_ttl, paste_to_file, allow_user_personas), dead PolicyDefaults removed (allow_raw_model_access, default_model), dead policy lookups removed (kb_direct_access), test seed data cleaned.
Packages surface Package list loads (17 total/17 enabled), type filters work, enable/disable visible, settings/export buttons present, core package (admin) protected. Removed dead chat from CORE_IDS.

v0.2.7 — User Settings Audit

Step Status Description
User settings E2E All 6 user settings sections verified (General, Appearance, Profile, Teams, Connections, Notifications). Dead code removed: BYOK nav section + state, personas gate filter, Message Font Size slider, auth.permissions.changed listener. localStorage key renamed cs-appearancesb-appearance with one-time migration.
Visibility gating Dead BYOK/personas policy lookups removed from bootstrap and permissions handlers. allow_user_byok removed from PublicSettings. PolicyDefaults cleaned of allow_user_byok and allow_user_personas. Dead msgFont early-apply removed from base template. Stale policy-gating test assertions replaced. No empty nav sections remain.

v0.2.8 — Team Admin Settings Audit (Pass 1)

Step Status Description
Team admin E2E Audited team member management, settings cascade, role assignment. Removed dead code: HasPrivateProviderRequirement (BYOK vestige), UserRole on TeamMember (deprecated role column), allow_team_providers policy default, dead personas/providers/models ICD tests.
Workflow stage UI cleanup Removed dead personas dropdown, history_mode selector, stale chat_only mode. Updated STAGE_MODES to match backend CHECK constraint (form_only, form_chat, review, custom). Removed stale comments referencing deleted files.

v0.2.9 — Builtin Extension Retirement

Step Status Description
Retire builtin seeder Removed SeedBuiltinPackages, seed_packages.go, Dockerfile COPY, and extensions/builtin/ directory. These extensions are dormant until a chat surface exists to consume them.
Convert to regular packages Repackaged 6 extensions as standard directories in packages/ with js/ layout and "requires": ["chat"] manifest metadata. Built via build.sh like all other packages. No auto-install — explicit install only.

v0.3.x — Workflow Architecture

Workflows are the core platform capability. This series implements the full multi-step automation system with team role integration and finalizes the extension lifecycle model.

v0.3.0 — Schema Redesign + Stage CRUD Modernization (complete)

Step Status Description
Workflow schema redesign Dropped persona_id, history_mode from workflow_stages. Renamed transition_rulesstage_config. Updated stage_mode CHECK to (form, review, delegated, automated). Added audience, stage_type, starlark_hook, branch_rules. Both PG + SQLite.
Model + store updates Go structs, constants, and PG/SQLite queries updated. New ValidStageTypes, ValidAudiences maps. Routing engine reads branch_rules directly.
Handler + Starlark updates Stage CRUD validation for new fields. Package export/import updated. Hook handler reads stage_config. Starlark module exposes audience, stage_type.
Frontend updates Team-admin and admin stage editors updated with new modes, audience selector, stage type selector, conditional Starlark hook input. Fixed admin STAGE_MODES bug.

v0.3.1 — Instance + Assignment Tables + Store (complete)

Step Status Description
Instance schema workflow_instances table in 007_workflows.sql (PG + SQLite). Tracks execution state: workflow_version, current_stage, stage_data, status, entry_token.
Assignment schema workflow_assignments table. Per-stage queue: instance_id, stage, team_id, assigned_to, status, review_data. Optimistic claim lock.
Models + store interface WorkflowInstance, WorkflowAssignment structs. 15 store methods for instance lifecycle and assignments across PG + SQLite.
Event types workflow.started, workflow.cancelled, workflow.error added to bus route table.

v0.3.2 — Workflow Engine + Handlers (complete)

Step Status Description
Store tests Round-trip tests for all 15 v0.3.1 store methods (instance + assignment CRUD). TruncateAll updated for new tables.
Stage execution engine server/workflow/engine.go — Start, Advance (with branch_rules), Cancel. Merges stage_data, creates assignments, emits events.
Automated stages server/workflow/automated.go — fire Starlark hook, auto-advance, cycle guard (max 10 consecutive).
Instance handlers HTTP API: Start, GetInstance, Advance, Cancel, ListInstances. Team-scoped mirrors.
Assignment handlers HTTP API: Claim, Unclaim, Complete, Cancel, ListByTeam, ListMine. Claimer identity verification.
Starlark module expansion workflow.get_instance(), workflow.list_instances() (read-only). Mutating builtins deferred pending interface extraction.

v0.3.3 — Public Entry + Background Jobs (complete)

Step Status Description
Public entry StartPublic, ResumePublic, AdvancePublic — token-based anonymous workflow participation. Public routes at /api/v1/public/workflows/. Audience-gated: only public stages can be advanced anonymously.
SLA scanner Background goroutine (5-min interval) checking active instances against per-stage sla_seconds. Fires workflow.sla_breach event, marks breach in instance metadata (idempotent).
Staleness sweep Per-workflow staleness_timeout_hours column. Scanner marks idle instances as stale, cancels open assignments, fires workflow.stale event.

v0.3.4 — Team Roles + Multi-party Validation (complete)

Step Status Description
Team roles Removed CHECK constraint on team_members.role. Custom roles stored in teams.settings["roles"]. Team roles API (GET/PUT /teams/:teamId/roles). Role-based stage assignment via stage_config.required_role. Frontend: dynamic role selects, role management panel.
Multi-party sign-off workflow_signoffs table (both dialects). StageConfig.validation with required_approvals, required_role, reject_action. Engine validation gate in advanceInternal. SubmitSignoff engine method. Signoff HTTP API (POST/GET /instances/:iid/signoffs). Frontend signoff panel in monitor tab.
Extension lifecycle Design doc: docs/DESIGN-EXTENSION-LIFECYCLE.md. Permanent vs PoC classification, graduation criteria, install model.
Trigger composition Design doc: docs/DESIGN-TRIGGER-COMPOSITION.md. Triggers/schedules can start workflows, workflows emit events, no circular invocation.

v0.3.5 — Settings Audit + ICD + Tests

Step Status Description
Clone endpoint POST /api/v1/workflows/:id/clone — deep copy workflow + stages. Handler-level composition of existing store methods.
Integration tests 7 engine-level tests in workflow_engine_test.go: full lifecycle, branch routing, public entry, signoff gate, rejection, cancel-clears-assignments, error cases. 28 total tests.
Settings audit pass 2 Added staleness_timeout_hours field to workflow editor. Added collapsible branch_rules JSON editor in stage form.
ICD update Fixed stale Workflow/Stage schemas. Added WorkflowInstance, WorkflowAssignment, WorkflowSignoff schemas. Added ~20 new endpoints: instances, assignments, signoffs, public entry, clone, team roles.

v0.3.6 — Example Workflows + Interactive Demo

Four installable .pkg workflow packages that prove the engine works end-to-end, plus a demo surface for guided exploration. See docs/DEMO-WORKFLOWS.md for full stage definitions and Starlark code.

Step Status Description
Bug Report Triage Public entry, progressive fieldsets, severity-based branch routing, team assignment, SLA timer. Pure-manifest .pkg.
Employee Onboarding Starlark automated stages (db.insert, notifications.send), signoff gate with required_role, rejection reroute. .pkg with script.star.
Content Approval Multi-party signoff (quorum of 2), rejection reroute creating review cycle. .pkg demonstrating signoff + reroute loop.
Webhook Notifier Starlark http.post + connections.get for outbound webhooks, delivery logging to ext_data. Proves HTTP + connections modules.
Demo surface Browser-tier walkthrough: workflow cards, stage diagrams, Starlark viewer, "Try It" buttons, API curl examples. Auto-installed, removable.
Engine context fix Add started_by to automated stage Starlark context dict (backward-compatible).
SLA package installer Added sla_seconds support to workflowPkgStage struct and install/export paths.
Snapshot format fix parseSnapshotStages helper handles both wrapped and legacy snapshot formats.
Workflow adoption POST /teams/:teamId/workflows/:id/adopt + GET .../available. Team-admin "Adopt Global" button. TeamID in WorkflowPatch.
Extension SDK boot base.html loads Preact globals + boot() for extension surfaces (was missing since Scorched Earth IV).
Admin teams tab fix Extract .data from paginated response in admin teams list.
Documentation docs/DEMO-WORKFLOWS.md: feature capability matrix, stage flows, API walkthrough, per-package READMEs.
Review pass Docker E2E walkthrough: install all packages, adopt into team, activate, publish, run each workflow end-to-end. Fix remaining UI/UX issues.

v0.3.7 — Package Audit (complete)

Verified all 16 packages install correctly. Packages with unmet requires auto-set to dormant.

Step Status Description
Manifest fixes Fixed 6 chat-extension manifests ("name""title", was blocking install). Added explicit "type": "surface" to hello-dashboard, icd-test-runner, sdk-test-runner.
Dormant status Added dormant to packages.status CHECK constraint (both dialects). Installer auto-detects unmet requires and sets status=dormant, enabled=false. Enable endpoint returns 409 for dormant packages.
Functional audit Navigated to every surface in browser. 7 working: schedules, tasks, team-activity-log, git-board, hello-dashboard, icd-test-runner, sdk-test-runner. 2 broken: dashboard + editor (depend on removed sw.* imperative SDK) — tagged requires: ["legacy-sdk"] → dormant.
Chat-dependency tagging 6 chat-dependent extensions (csv-table, diff-viewer, js-sandbox, katex-renderer, mermaid-renderer, regex-tester) install as dormant. git-board and gitea-client are standalone.
Dependency check fix Relaxed library dependency validation to allow pending_review libraries (gitea-client declares permissions). Only suspended/dormant libraries blocked.
Admin UI Dormant badge, disabled Enable button with tooltip, Dormant stat card in admin packages view.
Tests 4 handler tests (SetStatus dormant, enable blocked, surfaces exclude dormant, admin list includes dormant). All existing tests pass.

v0.3.8 — Distribution

Builder image for faster builds, bundled packages for zero-config first run.

Step Status Description
Builder image Dockerfile.builder with cached Go modules + Node vendor libs for faster custom builds.
Bundled packages Dockerfile stage builds all 12 non-dormant packages into production image. InstallBundledPackages auto-installs on first run (skip-if-present). SKIP_BUNDLED_PACKAGES=true to disable. BUNDLED_PACKAGES allowlist for selective install (Helm/K8s). Migration 012 adds bundled source to CHECK constraint.
Distribution docs docs/DISTRIBUTION.md — quick start, bundled packages, builder image, custom builds, production deployment.
Tests 6 handler tests (fresh install, skip existing, missing dir, empty dir, dormant handling, allowlist filtering). All existing tests pass.

v0.4.x — Notes Surface

Obsidian-style notes rebuilt as an installable surface package. Zero platform special-casing. Proves the full extension stack E2E.

v0.4.0 — Core Notes CRUD + Markdown Editor

Step Status Description
Package scaffold packages/notes/ with manifest, script.star, JS, CSS, README. Type: full, tier: starlark.
Notes CRUD backend Starlark on_request() — list, create, get, update, delete, search, stats. Lightweight list projection (no body).
Notes table ext_notes_notes — title, body (TEXT), folder_id, pinned, archived, creator_id, updated_at.
Markdown editor Preact+htm frontend: sidebar note list, title input, markdown textarea, auto-save (1s debounce).
Live preview Inline markdown renderer (~100 lines): headings, bold, italic, code, links, lists, blockquotes, hr.
Search Client-side LIKE search over title/body. Search bar in sidebar.
Pin/archive Pin notes to top, soft-delete via archive, hard delete with ?hard=1.

v0.4.1 — Folders + Navigation Tree (complete)

Step Status Description
Folders table ext_notes_folders — name, parent_id, creator_id, sort_order. Indexed on parent_id and creator_id.
Folder CRUD API 5 Starlark handlers: list, create, update, delete folders + POST /notes/move. Delete cascade orphans notes and reparents child folders.
Folder tree UI FolderTree + FolderNode components. Flat-to-tree builder, expand/collapse, depth indentation, inline rename via context menu.
Folder filtering Click folder → filter notes. "All Notes" / "Unfiled" virtual views. New notes inherit active folder.
Editor folder select Dropdown with nested hierarchy ( prefix). Moves notes between folders via move-note API.
Updated stats Stats include unfiled and folders counts.

v0.4.2 — Tags + Search (complete)

Step Status Description
Tags table ext_notes_tags — note_id, tag. Indexed on both columns for bidirectional lookup.
Tag CRUD API 3 Starlark handlers: list all unique tags, get tags for note, replace tags for note (delete+reinsert). Tags normalized: lowercase, trimmed, deduped.
Tags in list/get/search _list_notes batch-fetches all tags in one query, attaches tags array to each item. _get_note includes tags. _search_notes matches against tags.
Tag cascade delete Hard-deleting a note also deletes its tag rows. Stats include tags count.
Tag input in editor TagInput component: removable pills + text field, comma/Enter to add, autocomplete dropdown from all tags.
Tag pills on cards NoteCard renders up to 3 tag pills with "+N" overflow.
Tag filter in sidebar TagFilter component: clickable pills, toggle active tag, client-side note filtering.
Drag-and-drop NoteCard is draggable. FolderNode, "All Notes", and "Unfiled" are drop targets. Uses existing move-note API.
Folder context menu Replaced prompt() hack with proper right-click popup menu: Add subfolder, Rename, Delete. Positioned at click coordinates, dismissed on outside click.
Step Status Description
Links table ext_notes_links — source_id, target_id, link_text. Indexed on both source_id and target_id for bidirectional lookup.
Wikilink extraction _extract_wikilinks() parses [[...]] using split("[[") + find("]]") (no regex/while in Starlark). Deduplicates by lowercase.
Link sync on save _sync_links() called from _create_note and _update_note. Delete-all + reinsert pattern (matches tags). Resolves titles to note IDs (case-insensitive). Unresolved links stored with target_id="".
Cascade delete Hard-deleting a note removes both outgoing links (source_id) and incoming backlinks (target_id).
Link endpoints GET /links/:note_id (outgoing) and GET /backlinks/:note_id (incoming, enriched with source titles).
Wikilink preview [[Note Title]] rendered as clickable accent-colored links in markdown preview. Unresolved links styled in danger/red.
Wikilink navigation Clicking a resolved wikilink navigates to the target note. Clicking an unresolved (red) link creates the note, navigates to it, and re-saves the source so the link resolves.
Backlinks panel Collapsible panel below editor showing all notes linking to current note. Click to navigate. Hidden when empty.
Stats update /stats includes links count. Notes package version bumped to 0.4.0.

v0.4.4 — Rich Editor + Import/Export (complete)

Step Status Description
CM6 integration Load vendored CodeMirror 6 bundle via dynamic <script> tag. CM.noteEditor() factory with markdown syntax highlighting, wikilink autocomplete, and live preview decorations. Textarea fallback if CM6 unavailable.
Editor wiring EditorPane uses CM6 with onChange → auto-save debounce, onLink → wikilink navigation, linkCompleter → search API autocomplete. Ctrl/Cmd+S force save. Editor fills container via CSS override.
Export as .md Export button in editor header. Downloads note as .md file with YAML frontmatter (title, tags, created). Pure client-side Blob download.
Import .md Import button in topbar. File picker for .md/.markdown/.txt. Parses YAML frontmatter for title and tags. Creates note via API with tags.
Notes package v0.5.0 Manifest version bumped from 0.4.0 → 0.5.0.

v0.4.5 — Editor Modes + Document Outline (complete)

Step Status Description
Default-rendered mode Preview is the initial state on note open. "Edit" button enters CM6. Tri-state viewMode: rendered / edit / split.
Split view Side-by-side layout: CM6 left, rendered preview right. CSS grid two-column in .notes-editor__body--split.
Synced scroll Split mode scroll sync. CM6 scrollDOM scroll listener maps position ratio to preview scrollTop. Linear mapping.
View mode setting editor_mode in manifest settings + localStorage persistence. Toolbar cycles rendered → edit → split.
Document outline parseHeadings() extracts headings (skips code blocks). Collapsible DocumentOutline panel with click-to-scroll (CM6 line dispatch or preview scrollIntoView). Debounced 300ms updates.

v0.4.6 — Sidebar Restructure (complete)

Step Status Description
Sidebar tabs Two-tab header at top of sidebar: "Notes" (folders → tags → search → list) and "Outline" (heading tree for active note). Outline tab enabled only when a note is selected.
Heading tree Nested heading hierarchy (H1 → H2 → H3) with depth indentation. Click → scroll to heading in editor/preview. Current heading highlighted based on scroll position.

v0.4.7 — Note Graph

Step Status Description
Graph API GET /graph endpoint in Starlark — { nodes: [{id, title, folder_id, tags}], edges: [{source, target, text}] }. Full notes + links in one payload.
Graph renderer Canvas force-directed layout. Minimal force simulation (repulsion + attraction + damping). Nodes = circles with title labels, edges = lines. Zoom/pan via wheel + drag. Retina-aware rendering.
Click → focus + filter Single click a node: dims unconnected nodes/edges to 15% opacity. Clicked node + direct neighbors stay full brightness. Edges highlighted in accent color. Click empty space to reset.
Shift+click → chain Shift+click adds a second node to the focus set — union of both neighborhoods visible. Trace thought paths across two hops without losing context.
Double-click → open Double-click navigates to the note and exits graph view, opening the editor with the selected note.
Hover → tooltip Hover shows note title + tag pills + edge count in HTML overlay. No API call — all data in graph payload.
Folder/tag coloring Nodes colored by folder using 10-color palette. Active tag filter dims non-matching nodes to 30% opacity. Stacks with click focus.
Orphan highlighting Zero-edge nodes rendered with dashed stroke. "Hide orphans" checkbox toggle in toolbar.
Entry point "Graph" button in topbar. Replaces editor pane with full graph canvas. Single-click selects note in sidebar; double-click opens editor.

v0.5.x — Realtime + Chat

Communication primitive track. The kernel gains one generic realtime module. Everything else — conversations, messages, participants — is pure extension code. Human-to-human chat ships pre-MVP; LLM participation is post-MVP.

v0.5.0 — Realtime Primitive + Dialog Audit + Permissions UI

Step Status Description
realtime.publish() Starlark module: realtime.publish(channel, event, data). Publishes JSON event to WebSocket hub scoped to channel. Gated by realtime.publish permission. 7KB payload limit. Reserved prefix validation.
WS room protocol room.subscribe / room.unsubscribe client messages intercepted in readPump. Per-connection 100-room cap.
sw.realtime.subscribe() SDK method: sw.realtime.subscribe(channel, [event], callback). Auto room join/leave, reconnect recovery. Returns unsubscribe handle.
sw.ui.Dialog Pre-existing (implemented before v0.5.0). SDK modal primitive with focus trap, Promise-based API.
sw.ui.Confirm / sw.ui.Prompt Pre-existing. sw.confirm() and sw.prompt() already exposed in SDK.
Native dialog audit 5 bare confirm() calls migrated to sw.confirm() in tasks, schedules, notes (×2), editor packages.
Admin permissions UI Permissions drawer in admin Packages page. Grant/revoke per permission, grant-all bulk action. pending_review/suspended status badges. SDK API client methods added. Closes gap identified in v0.4.7.

v0.5.1 — Chat Core Library

Step Status Description
Library package chat-core library with permissions: ["db.write", "realtime.publish"]. Private ext_data tables, exported Starlark functions, REST endpoints.
Conversations table conversations — title, type (direct/group), created_by, updated_at.
Participants table participants — conversation_id, participant_id, participant_type (user/bot), display_name, role (member/admin), joined_at.
Messages table messages — conversation_id, participant_id, content, content_type (text/system/file), edited_at.
Read cursors table read_cursors — conversation_id, participant_id, last_read_message_id.
Exported Starlark API create(), send(), history(), add_participant(), remove_participant(), mark_read() — available via lib.require("chat-core").
REST endpoints 15 routes: full CRUD for conversations, messages, participants. Cursor-based paginated message history. Per-conversation unread counts.
Realtime integration On send(): insert message, publish realtime.publish("conversation:{id}", "message", data). Also emits participant.added, participant.removed, message.edited, message.deleted events.
db.query() range params Kernel enhancement: before/after kwargs for db.query() enabling cursor-based pagination and efficient unread counting. 7 new tests.
Admin Library filter Added Library type filter to admin Packages page alongside Surface/Extension/Full/Workflow.
Admin filter Dropdown Replaced button group with themed Dropdown primitive for type filter. Keyboard nav, consistent theming via CSS vars.
Unicode security gate ScanSource() detects variation selectors, bidi overrides, zero-width chars, tag characters. Verdict() blocks GlassWorm payloads and Trojan Source (CVE-2021-42574). Two enforcement points: install (422) + Starlark exec. 23 new tests.

v0.5.2 — Chat Surface

Step Status Description
Conversation list done Sidebar: conversation list with last message preview, unread badge, timestamp. Create conversation button.
Message thread done Main pane: message history with participant avatars, timestamps, content. Scroll-to-load-more for long threads.
Compose bar done Text input with Enter-to-send, Shift+Enter for newline. Auto-resize textarea.
Participant sidebar done Collapsible right panel: participant list with online status (via kernel presence hub). Add/remove participants using sw.ui.Dialog.
1:1 and group done Direct messages (two participants) and group conversations. Type field on conversation.
Typing indicators done realtime.publish("conversation:{id}", "typing", {participant_id}) on keypress with debounce. Surface shows "X is typing…" with auto-expire.
Read receipts done Mark-read on scroll/focus via chat.mark_read(). Unread count in conversation list.
Message editing done Edit own messages. edited_at timestamp displayed. Edit via PUT /messages/:id.
Message deletion done Soft-delete own messages. Replaced with "message deleted" placeholder.

v0.5.3 — Chat Polish + Integration Testing

Step Status Description
db.query() search_like Kernel enhancement: search_like kwarg on db.query() — generates OR-joined LIKE (SQLite) / ILIKE (Postgres) clauses. Reusable by any package. 5 new tests.
Conversation search GET /search?q=term endpoint in chat-core. Searches conversation titles and message content across user's conversations. Debounced search UI in sidebar with conversation + message results.
Message pagination polish Scroll position preservation on load-more (records scrollHeight, restores via requestAnimationFrame). Loading spinner at top during fetch. has_more button hidden while loading.
Multi-user E2E docker-compose-e2e.yml: 2 replicas + Postgres + nginx LB. ci/e2e-chat-test.sh: auth, create, send, cross-replica read, search, pagination. ci/e2e-ws-listener.js for realtime event testing.
Workflow integration workflow-chat library package: on_advance hook creates scoped group conversation, adds team members, sends system message, enriches stage_data with conversation_id. Idempotent. 6 new hook tests.

Backlog — Admin UI Polish

Item Description
"Requires Review" filter Dropdown filter option or dedicated button for pending_review packages. Useful when the package list grows long.
Extreme UI scale polish At 150%+ scale: user menu items overflow viewport (need scroll), topbar elements clipped. Ensure all surfaces and menus are usable at scale extremes (80%200%).

v0.5.4 — Package Updates

Step Status Description
Curate bundled packages Default set reduced from 23 to 8 core packages. BUNDLED_PACKAGES=* for all; explicit list for custom.
Version comparison Semver parsing (ParseSemver) with prerelease support. Version bump validated on update.
Schema migration on update MigrateExtTables diffs db_tables against existing schema. Adds new columns/tables. No destructive changes.
Data preservation ext_data tables survive update. Settings merged (new keys added with defaults, existing preserved).
Update API POST /api/v1/admin/packages/:id/update — accepts .pkg archive, validates version bump, applies schema diff, replaces code.
Admin UI Update button on non-core package cards. Confirm dialog before upload.
Export API GET /api/v1/admin/packages/:id/export — streams installed package as .pkg ZIP. Manual rollback: export → update → re-install old .pkg if needed.

v0.5.5 — Upgrade Testing

Step Status Description
Test harness docker-compose-upgrade.yml + ci/e2e-upgrade-test.sh: build v(old) image, seed notes/conversations/settings, upgrade to v(new), verify data integrity.
Schema edge cases 11 Go unit tests: add column (idempotent), add table, add index, multi-column add, row preservation across migration. All pass on SQLite.
Settings migration Go tests verify: global/team/user overrides preserved across package update, new keys get defaults, removed keys not deleted from DB.
Package compatibility Go tests verify: bundled skip-if-present on restart, dormant status for unmet requires, enabled/type preserved across version bump.
Multi-replica upgrade ci/e2e-upgrade-rolling.sh: rolling upgrade with 2 replicas + shared Postgres, cross-replica reads during mixed-version window, pg_notify fan-out verified.

v0.6.0 — MVP

Extension, communication, and operations tracks converge. First externally usable release.

Design docs: docs/DESIGN-cluster-registry.md — PG-backed cluster registry and self-assembling mesh.

v0.6.0 — Cluster Registry + HA (planned)

PG is the consensus layer. Zero new infrastructure. UNLOGGED table + LISTEN/NOTIFY replaces etcd/Consul/Redis for homelab-to-small-team scale.

Step Status Description
node_registry table UNLOGGED TABLE — node_id, endpoint, seq, registered_at, heartbeat, stats JSONB. Folds into existing schema init.
Node registration Self-registration on startup: INSERT ... ON CONFLICT DO UPDATE. node_id = hostname-PID or CLUSTER_NODE_ID env override.
Heartbeat tick Every 10s: update own heartbeat + collect runtime stats (goroutines, heap, GC, uptime, ws_clients, extensions).
Stale sweep Every heartbeat tick: DELETE WHERE heartbeat < now() - 30s. All nodes run it — idempotent, no ring topology.
Self-eviction If heartbeat UPDATE returns 0 rows: node was swept by peer → log error + os.Exit(1). K8s restarts → re-register.
LISTEN/NOTIFY routing Durable events (messages, state changes) fan-out via pg_notify. All replicas receive, push to local WS subscribers, drop if irrelevant. Phase 1: ephemeral events (typing, presence) also via NOTIFY (8KB limit, ~60 bytes each).
Cluster API GET /api/admin/clusterSELECT node_id, endpoint, registered_at, heartbeat, stats FROM node_registry ORDER BY seq. Returns {data: [...]} envelope.
Admin cluster dashboard Extension renders one card per node: node_id, endpoint, uptime, ws_clients, heap, GC pause. JSONB stats — future keys render automatically, no schema migration.
Health endpoint GET /health includes cluster: {size, peers, heartbeat_age_ms}. Satisfies MVP health monitoring requirement.
Config CLUSTER_NODE_ID, CLUSTER_HEARTBEAT_INTERVAL (default 10s), CLUSTER_STALE_THRESHOLD (default 30s), CLUSTER_ENDPOINT (Phase 2 mesh, auto-detect).
Single-node regression One-node behavior identical to pre-cluster: one registry row, NOTIFY delivers back to same instance. No special-casing.
Multi-node integration test Docker Compose: 3 instances, shared PG. Verify registration, heartbeat, peer discovery, stale sweep on shutdown, WS fan-out.

v0.6.1 — Backup/Restore + Documentation (planned)

Step Status Description
Backup tooling ext_data export/import, admin surface for backup management.
Documentation site Extension authoring guide, API reference, deployment guide.

Post-MVP

  • LLM participation (llm-bridge extension: subscribes to chat.message.created, calls provider.complete(), streams response via realtime.publish, posts via chat.send(). Bot participants with persona config. Multi-model conversations.)
  • Rich media extensions: image generation, code sandbox, STT/TTS
  • Desktop app (Tauri or Electron)
  • Sidecar tier: container-based extensions
  • Federation: cross-instance package sharing
  • Plugin marketplace with signing and review
  • Mermaid diagrams extension (nice-to-have)

Design Decisions Log

Decision Rationale
Tasks → extension Scheduler was the most entangled kernel component (~3,400 lines). Rebuilding as extension validates the trigger system and removes the worst compilation debt. Three trigger primitives (time, webhook, event) replace the monolithic scheduler.
Sessions removed Channel-based sessions coupled to deleted chat system. Workflow instances need new storage model — either ext_data tables or a dedicated kernel table.
chat_onlycustom Stage mode chat_only implied chat as a kernel concept. Renamed to custom which delegates to a surface package, proving extension composability.
Providers removed from kernel Provider configs, model catalog, routing policies — all moved to extension track. Kernel provides credential storage (connections) and the Starlark provider.complete module as the interface.
Kernel permissions simplified From 16 chat-centric permissions to 6 platform permissions. Extensions define their own capability requirements in manifests.
Preact+htm retained 3KB runtime, no build step, works for extension authors without bundler config. KISS.
Single Docker image Drop the frontend/backend split. Go binary + assets + migrations in one image. Simpler deployment, fewer moving parts.
Admin → RBAC group The role column is pre-RBAC. v0.2.0 replaces it with a seeded "Admins" group + surface.admin.access grant. All users auto-join "Everyone" group. Admin middleware becomes a grant check, not a role check.
Settings cascade RBAC controls scope auth (who can set at what level). user_overridable flag controls whether lower scopes can override higher. Two orthogonal axes, composes cleanly with extension manifests.
No new migrations pre-MVP Edit existing migration SQL files in place. No migration chains until schema is in production.
Notes over Editor First surface is Obsidian-style notes (rich text, folders, backlinks) instead of a code editor. Notes is a stronger E2E proof — it exercises ext_data, storage, and the SDK more fully than a pure-browser CM6 editor.
No built-in auto-install Extensions ship in the repo but are not auto-installed. Distribution model TBD — explicit install only. Keeps the kernel clean and avoids opinionated defaults.
Chat as extension, not kernel Human-to-human chat built entirely as library + surface packages. Zero kernel awareness of conversations, messages, or participants. Kernel gains one generic realtime module. Proves near-infinite extensibility. LLM participation layers on top via a separate bridge extension — the chat system doesn't know or care whether a participant is human or AI.
PG as consensus layer Horizontal scaling uses PG as the sole coordinator (UNLOGGED node_registry + LISTEN/NOTIFY). No etcd, Consul, Redis, or Raft. Rationale: system is already tightly coupled to PG; adding a second consensus layer doubles operational complexity for zero benefit at homelab-to-small-team scale. UNLOGGED table is visible to all connections, survives session disconnect, and cleans up automatically on PG crash — correct behavior since all nodes are dead anyway. Sweep-all for health (every node deletes stale rows) is simpler than ring topology and has no edge cases.
Two trigger tiers Event + webhook triggers are extension-declared (manifest contract, full sandbox). Scheduled tasks are user-created ad-hoc (restricted sandbox — no raw HTTP, no DB table creation, connections-only outbound). Separation keeps extension contracts static and user automation safe.
Scheduled task identity Tasks run as their creator (RBAC-scoped). Admin-created tasks can opt into system context. Creator deactivation pauses the schedule. Ensures audit trail and permission boundaries.