14 new tests: version bump, rejection cases (same/older/core/type/id mismatch), schema add column/table with data preservation, settings merge, export with valid zip, default allowlist, wildcard allowlist. Existing bundled tests updated for curated default behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
476 lines
43 KiB
Markdown
476 lines
43 KiB
Markdown
# 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-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. |
|
||
|
||
### 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 (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.
|
||
|
||
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/cluster` — `SELECT 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_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. |
|
||
| 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. | |