Switchboard Core — Roadmap
Current: v0.5.0 — Realtime Primitive + Dialog Audit + Permissions UI
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-switchboard → switchboard-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-appearance → sb-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_rules → stage_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. |
v0.4.3 — Backlinks + Wikilinks (complete)
| 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. |
| 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 (planned)
| Step |
Status |
Description |
| Library package |
|
chat-core library with permissions: ["db.write"]. 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 |
|
chat.create(), chat.send(), chat.history(), chat.add_participant(), chat.remove_participant(), chat.mark_read(). |
| REST endpoints |
|
Full CRUD for conversations, messages, participants. Paginated message history. Unread counts. |
| Realtime integration |
|
On chat.send(): insert message, emit chat.message.created event, call realtime.publish("conversation:{id}", "message", data). |
v0.5.2 — Chat Surface (planned)
| Step |
Status |
Description |
| Conversation list |
|
Sidebar: conversation list with last message preview, unread badge, timestamp. Create conversation button. |
| Message thread |
|
Main pane: message history with participant avatars, timestamps, content. Virtual scroll for long threads. |
| Compose bar |
|
Text input with Enter-to-send, Shift+Enter for newline. Markdown support via existing inline renderer. |
| Participant sidebar |
|
Collapsible right panel: participant list with online status (via kernel presence hub). Add/remove participants using sw.ui.Dialog. |
| 1:1 and group |
|
Direct messages (two participants) and group conversations. Type field on conversation. |
| Typing indicators |
|
realtime.publish("conversation:{id}", "typing", {participant_id}) on keypress with debounce. Surface shows "X is typing…" with auto-expire. |
| Read receipts |
|
Mark-read on scroll/focus via chat.mark_read(). Unread count in conversation list. |
| Message editing |
|
Edit own messages. edited_at timestamp displayed. Edit via PUT /messages/:id. |
| Message deletion |
|
Soft-delete own messages. Replaced with "message deleted" placeholder. |
v0.5.3 — Chat Polish + Integration Testing (planned)
| Step |
Status |
Description |
| Conversation search |
|
Search across conversation titles and message content. |
| Message pagination |
|
Cursor-based pagination on message history. Scroll-to-load-more in thread view. |
| Multi-user E2E |
|
Docker compose test: multiple browser sessions, verify real-time message delivery, presence indicators, cross-replica broadcast. |
| Workflow integration |
|
Verify: workflow stage with audience: team can create a conversation via chat.create(), add assigned members as participants. Conversation scoped to instance. |
v0.5.4 — Package Updates (planned)
| Step |
Status |
Description |
| Version comparison |
|
Compare installed package version against incoming .pkg version. Semantic version parsing. Skip if same or older. |
| Schema migration on update |
|
Diff declared db_tables against existing ext_data schema. Add new columns, add new tables. No destructive changes (no column drops, no type changes). |
| Data preservation |
|
ext_data tables survive update. Only additive schema changes applied. Settings merged (new keys added, existing preserved). |
| Update API |
|
PUT /api/v1/packages/:id/update — accepts .pkg archive, validates version bump, applies schema diff, replaces code (JS/CSS/Starlark). |
| Admin UI |
|
Update button on package card when newer version available. Version comparison badge. Update confirmation via sw.ui.Confirm. |
| Rollback story |
|
Export package before update (existing export). Document manual rollback: re-install old .pkg. No automatic rollback — KISS. |
v0.5.5 — Upgrade Testing (planned)
| Step |
Status |
Description |
| Test harness |
|
Docker compose environment: build v(current) image, seed data (notes, conversations, tasks, workflows), then upgrade to v(next) image. Verify data integrity post-upgrade. |
| Schema edge cases |
|
Force upgrade scenarios: add column to existing table, add new table, add new index. Verify ext_data migrations apply cleanly without data loss. Throwaway changes — not committed, only bug fixes. |
| Settings migration |
|
Verify settings cascade survives upgrade: global → team → user overrides preserved. New settings keys appear with defaults. Removed keys ignored. |
| Package compatibility |
|
Install older package version, upgrade kernel, verify package still loads. Install newer package version on older kernel, verify graceful failure. |
| Multi-replica upgrade |
|
Rolling upgrade in K8s: old and new replicas coexist briefly. Verify WebSocket hub handoff, no message loss, no split-brain on ext_data writes. |
v0.6.0 — MVP
Extension, communication, and operations tracks converge. First
externally usable release.
- Health monitoring (extension package wrapping kernel
/health)
- Backup/restore tooling (ext_data export/import, admin surface)
- 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_only → custom |
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. |
| 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. |