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/CHANGELOG.md
Jeffrey Smith 7adb47a50f
Some checks failed
CI/CD / detect-changes (pull_request) Successful in 20s
CI/CD / test-frontend (pull_request) Has been skipped
CI/CD / test-sqlite (pull_request) Successful in 2m39s
CI/CD / test-go-pg (pull_request) Failing after 2m54s
CI/CD / build-and-deploy (pull_request) Has been skipped
Feat v0.3.3 public entry + background jobs
Public workflow entry: unauthenticated routes at /api/v1/public/workflows/
for anonymous workflow participation. StartPublic creates instances with
public:<uuid> identity, ResumePublic/AdvancePublic use entry tokens.
Audience-gated: only public stages can be advanced anonymously.

SLA scanner: background goroutine (5-min interval) checks active instances
against per-stage sla_seconds. Fires workflow.sla_breach event on first
breach, marks sla_breached in instance metadata (idempotent).

Staleness sweep: new staleness_timeout_hours column on workflows. Scanner
marks idle instances as stale, cancels open assignments, fires
workflow.stale event.

New store methods: ListActiveInstances, MarkInstanceStale.
New events: workflow.sla_breach, workflow.stale.
3 new store tests (17 total), all passing on SQLite.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 22:28:22 +00:00

458 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
All notable changes to Switchboard Core are documented here.
## v0.3.3 — Public Entry + Background Jobs
### Added
- **Public workflow entry**: Unauthenticated routes for anonymous workflow
participation. `POST /api/v1/public/workflows/:id/start` creates an instance
with `started_by = "public:<uuid>"` and returns an entry token.
`GET .../resume/:token` and `POST .../advance/:token` allow continuation.
Only stages with `audience = "public"` can be advanced anonymously.
- **SLA scanner**: Background goroutine (5-minute interval) checks active
instances against per-stage `sla_seconds`. Fires `workflow.sla_breach` event
on first breach, marks `sla_breached: true` in instance metadata (idempotent).
- **Staleness sweep**: New `staleness_timeout_hours` column on `workflows`.
Scanner marks instances as `stale` when `updated_at` exceeds the threshold,
cancels open assignments, fires `workflow.stale` event.
- **New store methods**: `ListActiveInstances()`, `MarkInstanceStale()`.
- **New events**: `workflow.sla_breach`, `workflow.stale`.
- **3 new store tests**: MarkStale, ListActive, StalenessTimeoutHours round-trip.
---
## v0.3.0 — Workflow Schema Redesign
### Changed
- **`workflow_stages` schema modernized**: Dropped chat-era columns (`persona_id`,
`history_mode`). Renamed `transition_rules``stage_config`. Updated
`stage_mode` CHECK from `(form_only, form_chat, review, custom)` to
`(form, review, delegated, automated)`.
- **New stage fields**: Added `audience` (team | public | system), `stage_type`
(simple | dynamic | automated), `starlark_hook` (package_id:entry_point),
and `branch_rules` (JSONB array of routing conditions).
- **Routing engine**: `ResolveNextStage` now reads `branch_rules` (flat array)
instead of `transition_rules.conditions` (nested object). Cleaner separation
between routing rules and stage config.
- **Hook handler**: `FireOnAdvanceHook` reads from `stage_config` instead of
`transition_rules`. Renamed `channelID` parameter to `instanceID`.
- **Stage CRUD validation**: New fields validated on create/update. `delegated`
mode requires `surface_pkg_id`. `dynamic`/`automated` stage_type requires
`starlark_hook`.
- **Package export/import**: Updated to use new field names. Workflow packages
now include `audience`, `stage_type`, `starlark_hook`, `branch_rules`,
`stage_config`.
- **Starlark workflow module**: Stage dicts now include `audience` and
`stage_type` keys. Removed `history_mode` and `persona_id`.
- **Frontend stage editor**: Both admin and team-admin surfaces updated with
new mode values, audience selector, stage type selector, and conditional
Starlark hook input. Fixed pre-existing bug in admin workflows.js where
`STAGE_MODES` still contained `chat_only`.
### Removed
- `persona_id` column from `workflow_stages` (personas are extension concerns)
- `history_mode` column from `workflow_stages` (chat-era context management)
- `StageModeCustom`, `StageModeFormOnly`, `StageModeFormChat` Go constants
---
## v0.2.9 — Builtin Extension Retirement
### Changed
- **6 builtin extensions → standard packages**: csv-table, diff-viewer,
js-sandbox, katex-renderer, mermaid-renderer, and regex-tester moved from
`extensions/builtin/` to `packages/` with standard `js/` layout. Each
manifest now declares `"requires": ["chat"]` and `"type": "extension"`.
Built via `build.sh` like all other packages — no auto-install.
### Removed
- **`SeedBuiltinPackages` seeder**: Deleted `seed_packages.go` (function,
`builtinManifest` struct, `buildFullManifest` helper) and the startup call
in `main.go`. Extensions are no longer auto-seeded into the DB.
- **`extensions/builtin/` directory**: Removed from repo and Dockerfile COPY.
- **Seed tests**: Removed 8 `TestSeed_*` functions and `makeSeedDir` helper
from `extension_test.go` (~220 lines).
- **Stale comments**: Removed `SeedBuiltinPackages` references from
`package_iface.go` and `trigger_sync.go`.
---
## v0.2.8 — Team Admin Settings Audit (Pass 1)
### Removed
- **Dead `HasPrivateProviderRequirement`**: Store method checked team settings
for `require_private_providers` (BYOK vestige). Removed from interface,
PostgreSQL, and SQLite implementations. No callers existed.
- **Dead `UserRole` field on `TeamMember`**: Joined `users.role` column
(deprecated in v0.2.0 RBAC migration). Removed from model, `ListMembers`
and `GetMember` queries in both stores. Frontend never consumed it.
- **Dead `allow_team_providers` policy**: Removed from `PolicyDefaults`, both
test seed data blocks, and ICD test assertions. No handler or UI read it.
- **Dead personas in workflow stage UI**: Removed `sw.api.teams.personas()`
call (endpoint doesn't exist), personas state, persona dropdown in
StageForm, and persona badge in stage list.
- **Dead `history_mode` in stage UI**: Removed history mode selector and state
from StageForm. Backend column retained for v0.3.x schema migration.
- **Dead ICD tests**: Removed assertions for `/teams/:teamId/personas`,
`/teams/:teamId/providers`, and `/teams/:teamId/models` — endpoints were
removed in Phase 0 fork.
- **Stale `chat_only` stage mode**: Frontend `STAGE_MODES` updated from
`['chat_only', 'form_only', 'form_chat', 'review']` to
`['form_only', 'form_chat', 'review', 'custom']` matching the backend
CHECK constraint.
- **Stale comments**: Removed references to deleted `team_providers.go`,
`personas.go`, and `apiconfigs.go` files.
---
## v0.2.7 — User Settings Audit
### Changed
- **localStorage namespace**: Renamed `cs-appearance` key to `sb-appearance`
across appearance settings, base template, and workflow template. One-time
migration preserves existing user preferences.
- **Policy-gating tests**: Replaced stale `allow_user_byok` / `allow_user_personas`
assertions with `allow_registration` check (the only policy still in admin UI).
### Removed
- **Dead BYOK nav section** in user settings: empty `BYOK_ITEMS` array,
`byokEnabled` state, "BYOK Enabled" footer badge, and the nav group
that rendered an empty section.
- **Dead personas gate**: `personasEnabled` state and `.filter()` on
NAV_ITEMS for a `gate` property no items have. `auth.permissions.changed`
listener removed (existed solely for BYOK + personas state).
- **Dead Message Font Size**: Slider, `msgFont` state, and `--msg-font`
CSS variable application from appearance section and base template.
- **Dead policy defaults**: `allow_user_byok` and `allow_user_personas`
removed from `PolicyDefaults`, profile bootstrap, permissions handler,
and `PublicSettings`. Test seed data cleaned.
---
## v0.2.6 — Admin Settings Audit
### Changed
- **Roadmap reorder**: Extension Lifecycle moved from v0.2.6 to v0.3.x
(Workflows series). Settings audit milestones renumbered: v0.2.7→v0.2.6,
v0.2.8→v0.2.7, v0.2.9→v0.2.8. Added v0.2.9 for builtin extension
retirement (chat-centric extensions dormant until chat surface ships).
### Removed
- **Dead admin section categories** in `sectionCategory()`: AI (providers,
models, personas, roles, knowledgeBases, memory), routing (health, routing,
capabilities), and channels. Default category changed from `ai` to `system`.
- **Dead PublicSettings fields**: `system_prompt`/`has_admin_prompt`,
`retention_ttl_days`, `paste_to_file_chars`, `allow_user_personas` policy
— all chat-era with no frontend consumers.
- **Dead PolicyDefaults**: `allow_raw_model_access`, `default_model`.
- **Dead policy lookups**: `allow_raw_model_access` and `kb_direct_access`
from profile bootstrap and permissions handlers.
- **Dead test seed data**: `model_roles` global setting, `allow_raw_model_access`
policy from test helper.
- **Dead CORE_IDS entry**: Removed `chat` from packages admin page.
---
## [Unreleased] — v0.2.5
### Added
- **Welcome surface**: New core surface shown as fallback. Detects whether
extensions exist (shows "Set Default Surface") vs truly empty install
(shows "Go to Packages"). Topbar + UserMenu included.
- **User default surface**: Users can set a personal landing page in
Settings > General, overriding the admin-configured global default.
- **UserMenu in admin topbar**: Replaced Back button with UserMenu,
providing consistent surface navigation and eliminating infinite loops.
- **Package manifest icons**: Added emoji icons to hello-dashboard,
icd-test-runner, sdk-test-runner, and team-activity-log manifests.
- **PoC documentation**: README.md for tasks and schedules packages
documenting Proof of Concept status and graduation criteria.
### Changed
- **Default surface resolution**: Priority chain is now
user preference → global config → first extension → `/welcome`.
User preference read from JWT cookie on unauthenticated `/` route.
Correctly resolves `type: "full"` extension packages (not just `type: "surface"`).
- **User settings General section**: Replaced dead chat fields (Default
Model, System Prompt, Max Tokens, Temperature, Show Thinking) with
a Default Surface dropdown.
- **Admin settings**: Removed dead chat sections (System Prompt, Default
Model, Policies, Web Search, Auto-Compaction, Memory Extraction).
- **Roadmap restructured**: Workflows → v0.3.x (includes team roles),
Notes → v0.4.0, MVP → v0.5.0. Added settings audit milestones
(v0.2.7 admin, v0.2.8 user, v0.2.9 team-admin pass 1).
- Updated bus doc examples and test labels from `chat.*` to `workflow.*`.
- Renamed `channel-` prefixed test paths to `test-` in storage tests.
- Updated doc comments throughout to remove references to gutted surfaces.
### Removed
- **~500 lines of dead CSS**: Orphaned classes for gutted chat, channel,
project, notes, editor-chat, sidebar, and router-picker features from
`layout.css` and `surfaces.css`.
- **Dead Go types**: `Grant` struct (persona-era), `CompositeModelKey` func,
comment-only stubs for NoteGraph, ProjectChannel, etc.
- **Dead event code**: `chat.typing.*` / `channel.typing.*` condition in WS
subscriber, empty Chat/Channel event route table sections.
- **Dead test helpers**: `seed_helpers.go` (SeedTestMessage, SeedTestMessages,
SeedTestCursor — all callerless).
- **Stale template refs**: CSS link tags for non-existent `sw-chat-pane.css`,
`sw-notes-pane.css`, `chat.css`. Orphaned `chat-pane.html` component.
- **Vestigial guards**: `"chat"` surface checks in `IsSurfaceEnabled()` and
`DisablePackage()` (chat is no longer a surface).
- **Dead settings UI**: Chat defaults from user settings, System Prompt /
Default Model / Policies / Web Search / Compaction / Memory from admin.
- Unused `fmt.Sprintf` references in team stores.
---
## [Unreleased] — v0.2.4
### Added
- **`sw.shell.Topbar`**: Standard navigation bar component for surfaces.
Composes title + extension slot + NotificationBell + UserMenu into a
consistent 44px bar. Surfaces use `<${sw.shell.Topbar} title="...">` with
children rendered in the extension slot. Graceful fallback if unavailable.
- **Schedules surface** (`packages/schedules/`): New package wrapping the
kernel `/api/v1/schedules` API. Table view with cron badge + human-readable
preview, next fire time, enable/disable toggle, manual run, and execution
logs panel. Create/edit dialog with live cron-to-english preview.
- **Manifest `icon` field**: Packages can declare an emoji icon in
`manifest.json` (`"icon": "⏰"`). Served via the surfaces API `icon` field.
Rendered in the UserMenu flyout next to each surface name.
### Changed
- **UserMenu**: Surface list now driven entirely by the `/api/v1/surfaces`
API. Removed hardcoded Chat, Notes, Projects links (gutted in Phase 0).
Core surfaces (Admin, Settings, Team Admin, Workflow) filtered from the
API list and handled as dedicated menu items with RBAC gating.
- **Tasks surface**: Replaced custom `.tasks-header` with `sw.shell.Topbar`.
View tabs and create button rendered in the extension slot.
### Fixed
- **`sw.isAdmin` RBAC regression**: `isAdmin()` in `can.js` was checking the
deprecated `user.role === 'admin'` field instead of the v0.2.0 RBAC grant
`surface.admin.access`. Admin menu item and admin-gated features now appear
correctly for users in the Admins group.
---
## [Unreleased] — v0.2.2
### Added
- **Event bus subscriptions**: Extensions declare event triggers in manifest
(`"triggers": [{"type": "event", "pattern": "workflow.completed", ...}]`).
Wired via `bus.Subscribe()` on startup. Handlers fire asynchronously.
- **Webhook triggers**: Inbound HTTP at `/api/v1/hooks/:package_id/:slug`.
HMAC-SHA256 verification via `X-Switchboard-Signature` header. Synchronous
Starlark handler can return custom HTTP status and body.
- **Scheduled tasks**: User-created cron-scheduled Starlark scripts with
restricted sandbox (no raw HTTP, no DB table creation, connections-only
outbound). Runs as creator identity with RBAC scoping. Admin-created tasks
can opt into system context. Creator deactivation auto-pauses schedule.
- **Schedule templates**: Extensions ship pre-built schedule templates in
manifest (`schedule_templates` array) with configurable params and default
cron expressions.
- `triggers.register` extension permission — required for event/webhook triggers
- `triggers` table — extension-declared event and webhook trigger definitions
- `scheduled_tasks` table — user-created cron tasks with script, template,
and identity fields
- `trigger_logs` table — unified execution audit log for both tiers
- `TriggerStore` + `ScheduledTaskStore` interfaces (postgres + sqlite)
- Trigger engine (`server/triggers/`) — orchestrates event subscriptions,
webhook resolution, and cron scheduling via `robfig/cron/v3`
- `SyncManifestTriggers()` — declarative sync of event/webhook triggers from
manifest. Hooked into seed, admin install, and package install flows.
- Admin trigger API: `GET/PUT/DELETE /admin/triggers`, `/admin/triggers/:id/logs`,
`/admin/packages/:id/triggers`
- Admin schedule API: `GET /admin/schedules`, enable/disable/delete
- User schedule API: full CRUD at `/api/v1/schedules`, manual run, execution logs
- `trigger.fired` and `trigger.error` event bus labels (DirLocal) for observability
- OpenAPI spec: Trigger, ScheduledTask, TriggerLog schemas + all new endpoints
---
## [v0.2.1] — 2026-03-26
### Added
- **Default surface routing**: `/` redirects to configurable default surface.
Fallback chain: configured default → first enabled extension surface → `/admin`.
First installed extension surface auto-becomes default. Admin can change via
Settings > Default Surface dropdown.
- `default_surface` global config key (JSON `{"id": "slug"}`)
- Admin settings UI: Default Surface dropdown (extension surfaces only)
- **ICD (API contract)**: Full OpenAPI 3.0.3 spec covering all 160 kernel
endpoints. 22 tag groups (System, Auth, Profile, Workflows, Packages,
Connections, Teams, Groups, Extensions, and Admin subsections). Reusable
component schemas for User, Team, Group, Workflow, Package, Extension, etc.
Served at `/api/docs` (Swagger UI) and `/api/docs/openapi.yaml`.
### Changed
- `disabledRedirect()` now redirects to `/admin` instead of `/` to prevent
redirect loops when the default surface is disabled
- Disabled extension surfaces (`/s/:slug`) redirect to `/admin` instead of `/`
### Fixed
- **Gin route param conflict** causing backend startup hang: team-scoped
package settings routes used `:pkgId` while sibling routes used `:id`.
Gin's radix tree entered an infinite loop on the conflicting param names.
Unified to `:id` across all `/teams/:teamId/packages/` routes.
- Docker entrypoint: increased health check timeout (10s → 60s), added stale
process cleanup and crash detection to prevent zombie backends on restart
---
## [v0.2.0] — 2026-03-26
### Added
- **Full RBAC**: all authorization flows through group membership and permission
grants. No magic roles, no implicit group membership, no special-casing.
- `surface.admin.access` permission — any group can grant admin panel access
- Admins system group seeded with all platform permissions
- Everyone system group — all users explicitly added on creation
- `EnsureEveryoneGroup()`, `AddToAdminsGroup()`, `RemoveFromAdminsGroup()` helpers
- `SeedAdminsGroupMember()`, `SeedEveryoneGroupMember()` test helpers
- System groups re-seeded after `TruncateAll` in test helper
- OIDC `isIdPAdmin()` — maps IdP role claims to Admins group membership
- **Settings cascade**: three-tier resolution (global → team → user) with
`user_overridable` flag per manifest setting key. Admins can lock settings
that team admins and users cannot override.
- `package_team_settings` table for team-scoped package setting overrides
- Team admin API: `GET/PUT/DELETE /api/v1/teams/:teamId/packages/:pkgId/settings`
- `RunContext.TeamID` for team-aware Starlark settings resolution
- `store.ResolveSettings()` / `store.FilterOverridableKeys()` pure functions
- `store.ParseSettingsSchema()` extracts `user_overridable` from manifests
### Changed
- `RequireAdmin()` / `RequireAdminPage()` check `surface.admin.access` grant
- `RequirePermission()` no longer bypasses for admin role
- `ResolvePermissions()` unions explicit group memberships only (no implicit Everyone)
- All user creation paths (builtin, OIDC, mTLS, admin, bootstrap, seed) add to
Everyone group. Admin users added to Admins group.
- JWT claims no longer include `role` field
- Login response no longer includes `role` in user object
- Profile endpoint no longer returns `role`
- Profile bootstrap resolves permissions from groups (no admin shortcut)
- Middleware auth cache tracks `isActive` only (no role)
- Admin create user accepts `is_admin` bool (not role string)
- Admin update role endpoint accepts `is_admin` bool, manages Admins group directly
- Demotion/deletion safeguards check Admins group member count
- Notifications `RoleFallbackHandler` queries Admins group members
- OIDC syncs Admins group on login (no role column writes)
- Kernel permissions: 6 → 7 (added `surface.admin.access`)
- Admin users UI: role dropdown removed, admin managed through groups
- Starlark `settings.get()` uses cascade resolver instead of naive merge
- User settings save (`POST /extensions/:id/settings`) strips non-overridable keys
### Removed
- **`users.role` column** — dropped from schema, model, JWT, all handlers
- `UserRoleAdmin`, `UserRoleUser` constants
- `CountByRole()` store method
- `DefaultRole` config for OIDC and mTLS providers
- `SyncAdminsGroupMembership()` (replaced by `AddToAdminsGroup`/`RemoveFromAdminsGroup`)
- OIDC `resolveRole()` (replaced by `isIdPAdmin()`)
- **Token budgets** from groups: columns, `ResolveTokenBudget()`, all store/handler/UI
- **Allowed models** from groups: column, `ResolveModelAllowlist()`, UI
- Admin groups UI: Token Budgets section, Allowed Models section
### Migration notes
- 001_core.sql (both dialects): removed `role` column from users table
- 002_teams.sql (both dialects): added Admins group seed, removed
`token_budget_daily`, `token_budget_monthly`, `allowed_models` columns
- No new migration files — edited in place per pre-MVP policy
---
## [v0.1.0] — 2026-03-26
Forked from chat-switchboard v0.38.5. Gutted to a pure extension platform.
### Removed
- **AI/Chat system**: providers, model catalog, routing policies, personas,
channels, messages, completion streaming, tool loop, compaction, memory,
knowledge bases, notes, workspaces, projects, folders, files, export/import
- **Task scheduler**: entire scheduler package, task store, task handlers.
Tasks will be rebuilt as a Starlark extension with three trigger primitives
(time, webhook, event)
- **Session system**: channel-based anonymous sessions. Workflow instances
will get new storage in v0.2.0
- **Health accumulator**: provider health windows, tool health tracking.
Replaced with kernel-only Prune (ws_tickets, rate_limit_counters, presence)
- **15 Go packages**: tools, compaction, extraction, roles, mentions,
notelinks, export, memory, knowledge, providers, routing, capabilities,
filters, retention, workspace
- **29 handler files**, 6 test files, ~44K lines total
### Fixed
- CI deploy: k8s resource quantity vars (`BE_MEMORY_REQUEST``MEMORY_REQUEST`)
aligned with CI workflow outputs — `envsubst` was producing empty strings
- CI deploy: image var (`BE_IMAGE``IMAGE`) — caused `InvalidImageName` in pods
- CI rollout: deployment name (`switchboard``switchboard-be`) — rollout
verification was looking for wrong deployment name
- Nginx BASE_PATH: regex cache-header locations intercepted static asset
requests before alias could strip the sub-path prefix — moved inside alias block
- Post-login blank page: dead Go template references (`surface-chat`,
`surface-notes`, `surface-projects`) caused html/template to silently
produce Content-Length: 0 responses
- Login branding: "Chat Switchboard" → "Switchboard Core", updated tagline
and feature pills to reflect platform pivot
### Changed
- Module renamed: `chat-switchboard``switchboard-core`
- VERSION: `0.1.0`
- Default DB name: `switchboard_core`
- Fresh migrations: 9 files × 2 dialects (postgres + sqlite), 27 tables
- Store interfaces: 40 → 20 (13 in interfaces.go + 7 in separate iface files)
- Stage modes: `chat_only` removed, `custom` added
- Task output modes: `channel|note|webhook``notification|webhook|log`
- Kernel permissions: 16 → 6 (`extension.use`, `extension.install`,
`workflow.create`, `workflow.submit`, `admin.view`, `token.unlimited`)
- Everyone group seed: `["extension.use","workflow.submit"]`
- Global settings seed: site name "Switchboard Core"
- Config: removed 7 dropped fields (SessionExpiryDays, WorkflowStaleHours,
ProviderAutoDisableThreshold, ExtractionConcurrency, etc.)
- Health stores rewritten: kernel-only Prune for stale tickets, counters, presence
- Maintenance goroutine replaces scheduler for background cleanup
### Retained
- Identity & auth (builtin, mTLS, OIDC)
- Teams, groups, permissions
- Package system (surfaces, extensions, libraries, workflows)
- Starlark sandbox with capability-gated modules
- Extension connections & dependencies
- Workflow definitions, stages, versions
- Notifications & preferences
- Audit log
- Object storage (PVC, S3)
- WebSocket hub & presence
- Multi-replica HA (ws_tickets, rate_limit_counters)
- Frontend shell (preact+htm, SDK, vendor libs)