Changeset 0.26.0 (#165)

This commit is contained in:
2026-03-10 13:38:01 +00:00
parent dbc1a97343
commit 400f7dd176
48 changed files with 4923 additions and 208 deletions

View File

@@ -1,7 +1,210 @@
# Changelog
All notable changes to Chat Switchboard.
# Changelog Additions — v0.26.0
## [0.26.0] — 2026-03-10
### Summary
Workflow engine — team-owned staged processes with AI intake, visitor-facing
branded entry, human assignment queue, and admin builder UI. Channels become
execution contexts that move through defined stages, driven by personas and
monitored by team members.
### Added
#### Workflow Definitions (v0.26.1)
- **Workflow CRUD.** `POST/GET/PATCH/DELETE /api/v1/workflows` with name, slug
(auto-generated from name if omitted), description, entry_mode
(`authenticated` | `public_link`), is_active flag, branding JSONB, retention
JSONB. `UNIQUE(team_id, slug)` with partial index for global workflows.
- **Stage CRUD.** `POST/GET/PUT/DELETE /api/v1/workflows/:id/stages/:sid`.
Stages have name, ordinal, persona_id, system_prompt, form_template (JSONB),
history_mode (`full` | `summary` | `fresh`), assignment_team_id.
- **Stage reorder.** `PATCH /api/v1/workflows/:id/stages/reorder` — transactional
ordinal update from ordered_ids array.
- **Publish + versioning.** `POST /api/v1/workflows/:id/publish` creates an
immutable `workflow_versions` snapshot (stages + persona tool grants).
`GET /api/v1/workflows/:id/versions/:version` retrieves any published version.
Duplicate publish returns 409. Schema: `workflows`, `workflow_stages`,
`workflow_versions` (migration 023).
- **Permission gating.** Workflow create/update/delete requires `workflow.create`
permission (existing permission constant).
#### Workflow Instances + Stage Transitions (v0.26.2)
- **Start instance.** `POST /api/v1/workflows/:id/start` creates a workflow
channel, pins the published version, binds stage 0 persona, sets
allow_anonymous based on entry_mode. Returns channel_id + stage info.
- **Advance/reject/status.** `POST /channels/:id/workflow/advance` (with
optional form data merge), `POST /channels/:id/workflow/reject` (with
required reason → system message), `GET /channels/:id/workflow/status`.
- **Workflow columns on channels.** `workflow_id`, `workflow_version`,
`current_stage`, `stage_data` (JSONB), `workflow_status` (enum:
active/completed/stale/cancelled), `last_activity_at` (migration 024).
- **Staleness sweep.** Background goroutine (1h tick) marks active workflow
instances as `stale` when `last_activity_at` exceeds `WORKFLOW_STALE_HOURS`
(default: 72, env-configurable, 0 to disable).
- **Stage data merge.** Dialect-safe (Go-side JSON merge, no Postgres `||`
operator). Data accumulated across stages in `stage_data` column.
#### Visitor Experience (v0.26.3)
- **Workflow landing page.** `GET /w/:id/:slug` — public surface with branded
layout (accent color, logo URL, tagline from workflow branding JSONB),
persona name/icon, "Start" button, session resume link.
- **Visitor start flow.** `POST /api/v1/workflow-entry/:scope/:slug` — creates
channel + anonymous session, binds stage 0 persona, sets session cookie,
returns redirect to `/w/:channelId` (existing chat surface).
- **DenyVisitor tool scoping.** Workspace, git, memory tools already use
`DenyVisitor` predicate (shipped in v0.26.0 foundation).
#### AI Intake + Assignment Queue (v0.26.4)
- **`workflow_advance` tool.** AI-callable tool with `RequireWorkflow`
predicate — only available inside workflow channels. Persona calls this when
form data is collected. Triggers same transition as handler advance.
Accepts `data` (JSON object) + `summary` (string for stage note title).
- **Form template injection.** Completion handler injects current stage's
`form_template` as a system prompt when `channelType == "workflow"`. Tells
the persona what fields to collect and when to call `workflow_advance`.
- **Stage notes.** `CreateWorkflowStageNote()` persists collected form data as
channel-scoped notes (title: "Stage N: {summary}", content: JSON).
- **Assignment queue.** `workflow_assignments` table (migration 025): channel,
stage, team, assigned_to, status (unassigned/claimed/completed), timestamps.
Created automatically when advancing to a stage with `assignment_team_id`.
- **Assignment endpoints.** `GET /teams/:teamId/assignments` (team queue),
`GET /workflow-assignments/mine` (user's claimed), `POST /workflow-assignments/:id/claim`
(optimistic lock), `POST /workflow-assignments/:id/complete`.
#### Workflow Builder UI (v0.26.5)
- **Admin workflows section.** New "Workflows" category tab in admin surface.
List view with name, slug, entry mode, active status, version. Detail view
with editable fields, stage list, add/edit/delete stages, form template JSON
editor, publish button, public URL display.
- **Queue sidebar section.** "Queue" section in chat sidebar (after Channels).
Shows user's claimed assignments with badge count. Click to navigate to
workflow channel. Complete button per assignment.
- **Workflow API methods.** `workflow-api.js` extends global API object with
full workflow CRUD, stage management, publish, instance lifecycle, and
assignment queue operations.
- **Workflow CSS.** Stage row styling (draggable, ordinal badges), queue
sidebar items, team queue panel, admin badges.
#### Foundation (v0.26.0)
- **Session cleanup job.** Background goroutine deletes expired anonymous
sessions with no messages. `SESSION_EXPIRY_DAYS` env var (default: 30).
- **ExecutionContext TeamID wiring.** `teamID` propagated through completion,
stream_loop, and messages handlers into `ExecutionContext`.
- **Stale code cleanup.** Surface delete now removes static assets. Fixed
stale pricing comment. Removed deprecated `AllDefinitionsFiltered()`.
- **`workflow.create` permission.** Already in permission constants.
### Fixed
- **Gin route wildcard panic.** `/w/:id` (chat) and `/w/:scope/:slug` (landing)
used different param names at the same path depth — Gin panics on registration.
Fixed: landing route uses `/w/:id/:slug` (same param name, disambiguated by
depth). API visitor start moved to `/api/v1/workflow-entry/:scope/:slug`.
- **SQLite surface registry nil panic.** `SeedSurfaces()`, `IsSurfaceEnabled()`,
and `EnabledSurfaceIDs()` dereferenced nil Surfaces store on SQLite (no
migration 022). Added nil guards with graceful fallbacks.
### Migration Notes
- **DB wipe required.** Migrations 023-025 add new tables/columns. Fresh
`chat_switchboard_dev` DB recommended for dev deployments.
- **New env vars:** `SESSION_EXPIRY_DAYS` (default 30), `WORKFLOW_STALE_HOURS`
(default 72). Both optional.
- **Frontend files:** 3 new JS files (`workflow-api.js`, `workflow-admin.js`,
`workflow-queue.js`), 1 new CSS file (`workflow.css`). Templates updated
to include them.
### How to Create a Workflow
1. **Admin → Workflows** tab → "+ New" → enter name
2. **Edit** the workflow: set description, entry mode, toggle Active
3. **Add stages** with names, persona assignments, form templates (JSON),
history mode
4. **Publish** to create an immutable version snapshot
5. **Share** the public URL (`/w/{scope}/{slug}`) for visitor intake, or
start instances via API (`POST /workflows/:id/start`)
### Known Gaps (v0.27.0)
- Team-scoped workflow management UI (team admins can only see workflows via
admin panel, not the team settings surface)
- Drag-and-drop stage reorder in builder UI
- `on_complete` workflow chaining (column exists, nullable, not wired)
- Workflow retention policies (column exists, not enforced)
- Stage persona auto-switch in chat UI (backend binds persona, frontend
doesn't reflect stage transition yet)
## [0.25.4] — 2026-03-09
### Added
- **Admin settings export/import.** Export button downloads versioned JSON envelope (`_type`, `_version`, `_exported_at`, `_switchboard_version`, `settings`, `policies`). Import validates envelope, shows confirmation with key counts and metadata, writes each key via `adminUpdateSetting()`. Sensitive key warning on export.
- **Workspace rename/delete.** Workspace picker rows show ✏ rename and 🗑 delete buttons. `API.deleteWorkspace(id)` method added (backend `DELETE /workspaces/:id` already existed). Confirmation dialog warns about permanent file deletion.
- **Bulk model visibility.** Settings → Models page shows "X visible, Y hidden of Z total" with **Show All** / **Hide All** buttons.
- **Multi-provider live test failover.** `LIVE_PROVIDERS=venice,openrouter` env var. `tryCompletion()` walks providers until one responds 200. `setupAllProviders()` tolerates individual setup failures. CI no longer fails on transient upstream 503s.
### Fixed
- **Admin surface: 6 audit fixes.** Stray `>` in back button, dual-bind race on back button, hardcoded People nav flash, `closeAdmin()` ignores return URL, `openAdminSection()` pushes history, Storage section empty (missing `files.js` script).
- **Banner overlapping surface content.** Body changed to `display:flex;flex-direction:column;height:100vh`. Surface is `flex:1;min-height:0`. Banners get `flex-shrink:0`. Replaced fragile `calc(100vh - ...)` with proper flex distribution.
- **Scaling architecture.** `#surfaceInner` wrapper in `base.html` — generic scale target via `transform:scale()`. No surface-specific selectors. Banners stay unscaled. Extension surfaces get scaling for free.
- **Group permissions 500.** Postgres `UpdateBuilder.SetNull()` incremented `argIdx` without adding an arg — positional placeholders misaligned after first NULL column. SQLite version was already correct.
- **BYOK/Personas toggles not working.** Admin saves to policies store (`allow_user_byok`), settings loader read from `GlobalConfig` (`user_providers.enabled`). Two different stores, two different keys. Fixed to read from `Policies.GetAll()`.
- **Stale settings modal HTML.** 225 lines of orphaned modal body content from pre-surfaces migration rendered directly in chat page flow (General tab with `display:block` always visible below chat input). Removed along with old admin panel modal (~320 lines total).
- **Team admin modal transparent.** `class="modal-content modal-lg"``class="modal"`. Also fixed save-to-note modal in `notes.js`. Zero `modal-content` references remain project-wide.
- **Team admin modal layout.** Duplicate `display` property (`display:none;...;display:flex`) made content always visible. Moved flex to CSS class `.team-admin-content`.
- **Team persona create button missing.** Template lacked `settingsTeamAddPersonaBtn` button and `settingsTeamAddPersona` form container that JS expected.
- **Settings surface handler wiring.** `_initSettingsListeners()` early-returned on missing `settingsModel` (only on General section), killing all handlers below including BYOK provider form, model visibility, team admin add-member/provider/persona. Restructured: settings features under `data-surface` guard, team admin features unconditional with `?.`.
- **Settings surface missing `App.policies` and `App.models`.** On chat surface, `app.js` populates these. Settings surface called wrong API (`getSettings()` returns user prefs, not policies). Fixed to `getPublicSettings()` + `fetchModels()`.
- **Model Roles section empty.** Template had no `userRolesConfig` container for the roles section. `loadUserRoles()` early-returned on BYOK disabled or no personal providers with zero feedback. Added proper template section with notice text.
- **User settings NULL/null corruption.** `users.settings` column: SQL NULL → Postgres `COALESCE` fix. JSON `null` literal → `NULLIF(settings, 'null')` + `jsonb_typeof` guard. Prevents `null || {...} → [null, {...}]` array concatenation. Both `GetSettings` and `UpdateSettings` fixed for Postgres and SQLite.
- **`null.model_roles` crash.** Fresh users have NULL settings → `API.getSettings()` returned null → `settings.model_roles` threw. Added `|| {}` fallback on all callsites.
- **Project files 403 for admins.** `ListByProject`, `UploadToProject`, and `loadAndAuthorize` ownership checks had no admin bypass. Added `c.GetString("role") != "admin"` guard.
### Migration Notes
- **No new migrations.** All fixes are handler/template/JS changes.
- **Data fix required.** Users with corrupted settings (JSON array or null literal) need manual cleanup — see release notes.
## [0.25.3] — 2026-03-09
### Fixed
- **Theme system mode broken.** `Theme.set('system')` called `removeAttribute('data-theme')``:root` CSS hardcoded dark → system always showed dark. Now resolves OS preference via `matchMedia`, sets `data-theme` to resolved value, registers listener for OS changes.
- **Resize bar jump on click.** `onStart` now pins inline width/minWidth immediately after measuring via `getBoundingClientRect()`. Prevents snap-back during open-transition.
- **Debug modal no background/scroll.** `class="modal-content modal-lg"``class="modal modal-wide"`. Modal body restructured as flex column with proper overflow.
- **Settings/admin back button.** Stashes `document.referrer` in sessionStorage, nav links use `location.replace()`, back button reads stashed URL.
- **Profile save errors.** Wrong element ID (`settingsDisplayName``profileDisplayName`), wrong API routes (`/users/me``/profile`, `/auth/password``/profile/password`).
- **Profile data empty.** `getUserContext()` only populated ID and Role from JWT. Now fetches full user record.
- **Scale slider range.** `max="120"``max="175"`. Commit on mouse release, not live on input.
## [0.25.2] — 2026-03-09
### Added
- **Drag-resize utility.** `drag-resize.js` — single primitive for workspace handle and pane split handles. Full-viewport overlay prevents iframes/CM6 from swallowing mousemove. Touch support. Transitions killed during drag.
## [0.25.1] — 2026-03-09
### Fixed
- Minor surface integration fixes and CSS corrections from v0.25.0 deployment.
## [0.25.0] — 2026-03-08
### Added
- **Dynamic surfaces architecture.** Go template engine (`server/pages/`) with `base.html` shell, surface-specific templates, per-surface script/CSS blocks. Surface manifest registry with `SeedSurfaces()` for DB persistence.
- **Surface registry.** `surface_registry` table. Admin can enable/disable surfaces. Core surfaces: chat, admin, settings, editor, notes.
- **Component extraction.** UserMenu, ModelSelector, FileTree, CodeEditor, NoteEditor extracted as standalone components with Go template partials + JS hydration.
- **Pane container.** `pane-container.js` / `pane-container.css` — multi-pane layout with resizable splits. Used by editor surface.
- **Admin surface.** Full-page admin at `/admin/:section` replacing the old modal. Category tabs (People, AI, Routing, System, Monitoring), left nav, section-specific scaffolding via `admin-scaffold.js`.
- **Settings surface.** Full-page settings at `/settings/:section` replacing the old modal. Section-specific templates with server-rendered content areas.
- **Editor surface rebuild.** File tree pane + code editor pane + chat assist pane. Surface manifest declares three panes with resizable splits.
- **`base.html` template shell.** Banner → `#surface` → banner layout. Early theme flash prevention. Universal init block (`Theme.init()` + `UI.restoreAppearance()`). Common scripts: `app-state.js`, `api.js`, `events.js`, `ui-primitives.js`, `ui-core.js`.
- **Persona tool grants.** `renderPersonaForm()` gains tool grants section — "All tools" checkbox with categorized tool checklist. `loadToolList()` fetches from `/api/v1/tools`. `loadToolGrants()` reads persona-specific grants.
- **`window.__PAGE_DATA__`** injection from surface data loaders. `window.__USER__` from authenticated user context.
### Changed
- **Surface navigation.** `UI.openSettings()` and `UI.openAdmin()` navigate to full-page surfaces instead of opening modals.
- **CSS decomposition.** Monolithic `styles.css` split into: `variables.css`, `layout.css`, `primitives.css`, `modals.css`, `chat.css`, `panels.css`, `surfaces.css`, `splash.css`, `pane-container.css`, `chat-pane.css`, `user-menu.css`, `tool-grants.css`, `admin-surfaces.css`.
### Migration Notes
- **Migration 022:** `surface_registry` table (UUID PK, unique surface_id, is_enabled, is_core, created_at).
## [0.24.3] — 2026-03-07
### Added