# Changelog
## [0.38.5.0] — 2026-03-25
### Summary
Git-board rewrite — library composition capstone. Rewrites git-board from
a self-contained monolith into a library consumer of gitea-client, proving
the full extension architecture end-to-end: lib.require(), connections,
dependencies, multi-file Starlark, and connection type discovery. Includes
5 platform bug fixes found during composition testing.
### Changed
- **git-board v0.2.0:** Rewrote Starlark backend to delegate all Gitea API
work to gitea-client library via `lib.require("gitea-client")`. Removed
`api.http` permission, platform abstraction, and per-user token settings.
Added `connections.read` permission and `gitea-client >= 1.0.0` dependency.
Surface JS: replaced TokenSetup with ConnectionSetup screen.
(~130 lines, down from ~329)
- **gitea-client v1.0.1:** Added missing fields to `get_issues()` (created_at,
updated_at, html_url) and `get_prs()` (created_at, html_url, mergeable).
Re-exported loaded sub-module functions via explicit global assignment for
lib.require() compatibility.
### Fixed (platform bugs)
- **ext_api.go:** Route handler no longer requires `api.http` for all packages
with API routes. Consumer packages that delegate HTTP to libraries don't
need `api.http` themselves.
- **packages.go:** `InstallPackage` handler now calls `SyncManifestPermissions`
so permission rows are created on package install (was only in the legacy
`AdminInstallExtension` handler).
- **runner.go:** Package loader for `load()` sub-modules now injects the `json`
module into predeclared scope, matching ExecWithLoader behavior.
- **connection_resolver.go:** Added panic recovery around `vault.Decrypt` to
prevent goroutine crashes from invalid nonce lengths after vault key rotation.
### Added
- **SDK test runner:** `git-board.js` domain (14 tests) — validates multi-file
library install, connection type registration, consumer dependency, tool
registration, consumers list, uninstall protection, connection CRUD, full
cleanup cycle.
## [0.38.2.0] — 2026-03-25
### Summary
Library Packages. Reusable Starlark libraries that extensions can declare
as dependencies and load at runtime via `lib.require()`. Libraries export
functions, run with their own permission context, and are protected from
uninstall while active consumers exist. Includes a `test-tool` admin
endpoint for invoking extension tool calls without the AI chat loop.
### Added
- **Schema:** `ext_dependencies` table with `(consumer_id, library_id)`
composite PK, `version_spec` and `resolved_ver` columns. SQLite
migration also rebuilds `packages` table to add `'library'` to the
type CHECK constraint (SQLite cannot ALTER constraints).
(`server/database/migrations/023_ext_dependencies.sql`,
`server/database/migrations/sqlite/023_ext_dependencies.sql`)
- **Model:** `ExtDependency` struct with `ConsumerID`, `LibraryID`,
`VersionSpec`, `ResolvedVer`.
(`server/models/ext_dependency.go`)
- **Store:** `DependencyStore` interface — `Create`, `DeleteByConsumer`,
`ListByConsumer`, `ListByLibrary`, `ListAll`. Postgres and SQLite
implementations.
(`server/store/interfaces.go`, `postgres/ext_dependency.go`,
`sqlite/ext_dependency.go`)
- **Handlers:** Dependency admin endpoints:
- `GET /admin/packages/:id/dependencies` — list consumer's deps
- `GET /admin/packages/:id/consumers` — list library's consumers
- `GET /admin/dependencies` — full dependency graph
(`server/handlers/dependencies.go`)
- **Test-tool endpoint:** `POST /admin/packages/:id/test-tool` — invokes
a starlark extension's `on_tool_call` entry point directly for testing.
(`server/handlers/packages.go`)
- **Starlark module:** `lib.require(id)` — loads a declared library
dependency, executes its script with library-scoped permissions,
extracts manifest exports, freezes into a cached struct. Per-invocation
cache and cycle detection.
(`server/sandbox/lib_module.go`)
- **Install validation:** Library packages require `exports` array,
must not have `tools`, `pipes`, or `route`. Consumer `dependencies`
map validated against installed active libraries. Dependency records
created on install, cleaned up on consumer uninstall. Libraries with
active consumers return 409 on delete.
(`server/handlers/packages.go`)
- **SDK:** `sw.api.admin.packages.dependencies(id)`,
`sw.api.admin.packages.consumers(id)` domain methods.
(`src/js/sw/sdk/api-domains.js`)
- **ICD:** Extension dependencies section with all endpoint specs.
(`docs/ICD/extensions.md`)
- **OpenAPI:** Dependency endpoint schemas and `test-tool` endpoint.
(`server/static/openapi.yaml`)
- **SDK test runner:** `dependencies` domain — 8 tests covering library
install, dependency creation, consumer listing, uninstall protection,
cleanup, and library deletion.
(`packages/sdk-test-runner/js/domains/dependencies.js`)
### Changed
- **Runner:** `ExecPackage` creates a `libContext` per invocation and
passes it through `buildModulesWithLibCtx`. The `lib` module is always
injected (no permission required). Multi-file `packageLoader` reused
for library scripts.
(`server/sandbox/runner.go`)
- **Packages handler:** Install flow extended with dependency resolution
and `ext_dependencies` record creation. Delete flow checks for active
consumers before allowing library removal.
(`server/handlers/packages.go`)
### Design Notes
- `lib.require()` was originally `lib.load()` but `load` is a reserved
keyword in Starlark (used for `load("module.star", "symbol")`). Renamed
to `require` to avoid parser conflicts.
- Libraries run with their **own** permission context, not the consumer's.
A consumer without `api.http` can still call a library that uses
`http.get()` — the library's permissions are evaluated independently.
- The `test-tool` endpoint enables E2E testing of Starlark extensions
without a configured LLM provider.
### Files
- **8 created:** `lib_module.go`, `dependencies.go`, `ext_dependency.go`
(model), `ext_dependency.go` (pg), `ext_dependency.go` (sqlite),
`023_ext_dependencies.sql` (×2), `dependencies.js` (SDK runner)
- **11 modified:** `packages.go`, `runner.go`, `main.go`, `interfaces.go`,
`stores.go` (×2), `api-domains.js`, `openapi.yaml`, `extensions.md`,
`packages.js`, `main.js` (SDK runner)
- **+1,141 lines** (737 new files + 404 modified)
## [0.38.1.0] — 2026-03-25
### Summary
Extension Connections. Scoped credential management for extensions that
integrate with external services. Same scope/resolution pattern as LLM
providers (personal → team → global), sharable across packages.
### Added
- **Schema:** `ext_connections` table with `(type, scope, owner_id, name)`
unique constraint. Secret fields encrypted at rest using vault pattern.
(`server/database/migrations/022_ext_connections.sql`)
- **Store:** `ConnectionStore` interface with CRUD, scoped queries, and
resolution chain (personal → team → global).
(`server/store/interfaces.go`, `postgres/ext_connection.go`, `sqlite/ext_connection.go`)
- **Handlers:** Three scope tiers of REST endpoints:
- Personal: `GET/POST /connections`, `GET/PUT/DELETE /connections/:id`, `GET /connections/resolve`
- Team: `GET/POST /teams/:teamId/connections`, `PUT/DELETE /teams/:teamId/connections/:id`
- Admin: `GET/POST /admin/connections`, `PUT/DELETE /admin/connections/:id`
(`server/handlers/connections.go`, `team_connections.go`, `admin_connections.go`)
- **Starlark module:** `connections.get(type)`, `connections.get(type, name=)`,
`connections.list(type)` — gated by `connections.read` permission.
(`server/sandbox/connections_module.go`)
- **UI:** Connection management sections in Admin, Team Admin, and Settings
surfaces with dynamic form rendering from manifest field schemas.
(`src/js/sw/surfaces/admin/connections.js`, `team-admin/connections.js`, `settings/connections.js`)
- **SDK:** `sw.api.connections.*`, `sw.api.teams.{create,update,delete}Connection()`,
`sw.api.admin.connections.*` domain methods.
(`src/js/sw/sdk/api-domains.js`)
- **ICD:** Extension connections section with all endpoint specs.
(`docs/ICD/extensions.md`)
- **OpenAPI:** Connection endpoint schemas (personal, team, admin, resolve).
(`server/static/openapi.yaml`)
- **SDK test runner:** `connections` domain — CRUD + resolve chain tests.
(`packages/sdk-test-runner/js/domains/connections.js`)
## [0.38.0.0] — 2026-03-25
### Summary
Multi-file Starlark packages. Promotes Starlark scripts from a single
inline blob in the DB manifest to a proper file tree with `load()` support.
Prerequisite for library packages (v0.38.2) and large extensions.
### Changed
- **Extraction:** `extractableRelPath()` now extracts `star/` directories
and bare `*.star` files alongside `js/`, `css/`, `assets/`. Starlark
scripts land on disk at install time instead of being serialized into
the manifest JSONB.
(`server/handlers/packages.go`)
- **Sandbox:** New `ExecWithLoader()` accepts an optional `LoadFunc` for
resolving `load()` statements. `Exec()` is now a thin wrapper that
passes a nil loader (backward compatible).
(`server/sandbox/sandbox.go`)
- **Runner:** Disk-first script loading via `loadScript()` — reads from
`{packagesDir}/{pkgID}/script.star` (or custom `entry_point`), falls
back to legacy `_starlark_script` manifest field. Package-scoped
`packageLoader()` enables `load()` within a package's directory with
path traversal protection, `.star`-only enforcement, and circular
dependency detection.
(`server/sandbox/runner.go`)
- **Wiring:** `SetPackagesDir()` called at startup with
`cfg.StoragePath + "/packages"`. Build script includes `star/` in
archives.
(`server/main.go`, `packages/build.sh`)
### Added
- **Entry point validation:** Starlark-tier packages must include
`script.star` (or the manifest's `entry_point` value) in the archive.
Returns 400 if missing.
- **`entry_point` manifest field:** Optional override for the default
entry script name (`script.star`).
- **SDK test runner:** New `packages` domain with 6 tests covering
install validation (missing entry point, with star/, custom entry_point,
browser-tier bypass).
(`packages/sdk-test-runner/js/domains/packages.js`)
### Removed
- **`_starlark_script` injection:** The installer no longer reads
`script.star` from the archive and stuffs it into the manifest JSON.
Scripts are loaded from disk at runtime. Existing packages with
`_starlark_script` in their DB row continue to work (legacy fallback).
### Documentation
- **ICD:** Multi-file Starlark section added to `extensions.md` with
archive structure, `load()` constraints, and error codes.
- **OpenAPI:** Install endpoint description updated with entry point
validation and `star/` directory extraction.
- **Surfaces ICD:** Archive format updated to include `script.star` and
`star/` entries.
---
## [0.37.19.0] — 2026-03-24
### Summary
Tag release — "UI Complete." Closes all remaining code review items from
FE-REWRITE-REVIEW-0.37.14.md. Adds client-side RBAC gates (`sw.can()`) to
surface action buttons, migrates settings policy reads from `__PAGE_DATA__`
to `sw.auth.policies`, removes dead `__USER__` / `__PAGE_DATA__` template
injections, and moves shell-coupled `_getScale()` into the SDK. Light mode
CSS audit and dead code sweep confirm no outstanding issues.
### Changed
- **sw.can() RBAC gates (CR P2-1):** Channel/group creation buttons in chat
dashboard and sidebar gated behind `sw.can('channel.create')`. Persona
create/edit/delete gated behind `sw.can('persona.create')` /
`sw.can('persona.manage')`. Knowledge base create/upload/delete gated
behind `sw.can('kb.create')` / `sw.can('kb.write')`.
(`chat-workspace.js`, `sidebar.js`, `personas.js`, `knowledge.js`)
- **Settings → sw.auth.policies (CR P2-4):** Settings surface reads BYOK and
persona policies from `sw.auth.policies` (populated at SDK boot via
`/api/v1/profile/permissions`) instead of `window.__PAGE_DATA__`. Listens
for `auth.permissions.changed` event to react to policy updates.
(`settings/index.js`)
- **_getScale → sw.shell.getScale() (CR P3-3):** Scale detection for CSS zoom
compensation moved from sidebar-chats.js local function into SDK shell
namespace. Explicit coupling replaces implicit `#surfaceInner` DOM reach.
(`sdk/index.js`, `sidebar-chats.js`)
- **SDK version:** `sw._sdk` updated to `'0.37.19'`.
### Removed
- **__USER__ / __PAGE_DATA__ injection (CR P2-4):** Removed `window.__USER__`
and `window.__PAGE_DATA__` global injections from `base.html`. SDK boot
populates `sw.auth.user` and `sw.auth.policies` from API. Extension packages
updated to use SDK (`editor`, `hello-dashboard`, `icd-test-runner`).
(`base.html`, `loaders.go`, `EXTENSION-SURFACES.md`)
- **SettingsPageData policy fields:** `BYOKEnabled` and `UserPersonasEnabled`
removed from Go struct (no longer injected via template). (`loaders.go`)
### Documentation
- **CR P3-17:** Added comment documenting intentional raw DOM usage in notes
markdown wikilink processing (`markdown.js`).
- **CR P3-19:** Added comment documenting keyboard shortcut DOM presence check
pattern with future guidance (`use-notes.js`).
- **EXTENSION-SURFACES.md:** Updated `__USER__` references to `sw.auth.user`.
---
## [0.37.18.0] — 2026-03-24
### Summary
Debug/Model surface rebuild + workspace bridge. Debug modal converted from
673 lines of imperative DOM JS to a Preact component tree (CR P2-5). User
default workspace auto-created on first access. Tool outputs auto-save file
refs linked to assistant messages. "Save to workspace" action on chat code
blocks. Model selector shows provider health dots. Provider "Test Connection"
buttons added to both BYOK and admin UIs.
### Added
- **Debug modal Preact rebuild:** Engine singleton (`debugEngine`) with
subscribe() pattern for reactive updates. Component tree: ConsoleTab,
NetworkTab, StateTab, ReplTab, DebugBadge. `sw-debug.css` extracted from
`modals.css`. `debug.js` reduced from 673 to ~40 lines (thin bootstrap).
`repl.js` absorbed into `repl-tab.js`. (`src/js/sw/components/debug/`)
- **Default user workspace:** `GET /workspaces/default` auto-creates a
"My Files" workspace per user on first access. Idempotent. Route wired
before `:id` param. SDK: `sw.api.workspaces.getDefault()`.
(`server/handlers/workspaces.go`, `server/main.go`)
- **tool_output auto-save:** When `workspace_write` or `workspace_patch`
tools succeed during completion, a file ref with `origin=tool_output` is
recorded in the `files` table, linked to the assistant message via
`message_id`. (`server/handlers/tool_loop.go`, `completion.go`)
- **Origin filter on channel files:** `GET /channels/:id/files?origin=`
query param. SDK: `sw.api.channels.files(id, {origin})`.
(`server/handlers/files.go`)
- **Save-to-Workspace bridge:** Save button on assistant messages with code
blocks. Saves to user's default workspace with auto-generated filenames.
`guessExtension()` utility (37 language mappings). (`message-actions.js`,
`message-bubble.js`, `code-block.js`)
- **Model selector health dots:** Provider health status (green/yellow/red)
shown next to model names in chat selector. Admin-only, graceful degrade
for non-admin. (`model-selector.js`)
- **Provider test buttons:** "Test Connection" in Settings BYOK and "Test"
in Admin providers. Calls `fetchModels()` and shows success/failure toast.
(`settings/providers.js`, `admin/providers.js`)
### Changed
- `modals.css`: Debug-specific rules moved to `sw-debug.css` (~22 rules).
- `base.html`: 46-line debug modal HTML block replaced by `
`.
- `sw.js`: Service worker cache list updated (repl.js removed, sw-debug.css added).
- `LoopResult` struct: new `FileRefs` field for tool file output tracking.
- `channels.files` SDK method: now accepts optional opts for query params.
### Removed
- `src/js/repl.js` (527 lines) — absorbed into Preact `repl-tab.js`.
### ICD / OpenAPI / SDK Test Runner
- ICD: `GET /workspaces/default` documented; tool_output creation behavior updated.
- OpenAPI: `/api/v1/workspaces/default` endpoint added.
- SDK runner: 2 workspace tests (getDefault + idempotent), 1 channel files origin filter test.
## [0.37.17.0] — 2026-03-24
### Summary
Workspaces — workspace-first file storage for projects. Project files route
through the workspace FS subsystem (auto-created on first upload) instead of
the flat objStore path. Tree browser UI with nested folders, archive support
(zip/tar.gz upload and download), file actions (download, delete, mkdir), and
upload improvements (progress counter, archive detection, quota errors).
Architecture decision: `files` table + objStore = ephemeral message attachments;
Workspace FS = persistent file collections (tree, dirs, archive, quota, indexing).
"Save to workspace" bridge from chat files deferred to v0.37.18.
Also: extension track continuation scheduled as v0.39.x in roadmap, two new
design docs committed (multi-file Starlark, extension connections & libraries).
### Added
- **Workspace-backed project files:** `ensureProjectWorkspace()` auto-creates a
workspace (`owner_type=project`) on first file upload, sets `project.workspace_id`.
All project file operations route through workspace FS instead of objStore.
(`server/handlers/files.go`)
- **File download:** `GET /projects/:id/files/download?path=` streams raw file
content with `Content-Disposition: attachment`. (`server/handlers/files.go`)
- **File delete:** `DELETE /projects/:id/files?path=` with optional `recursive`
param for directories. (`server/handlers/files.go`)
- **Create directory:** `POST /projects/:id/files/mkdir` accepts path via JSON
body or query param. (`server/handlers/files.go`)
- **Archive upload:** `POST /projects/:id/archive/upload` extracts .zip/.tar.gz
into project workspace. Returns `files_extracted` count.
(`server/handlers/files.go`)
- **Archive download:** `GET /projects/:id/archive/download?format=zip` bundles
all project files into a zip/tar.gz. (`server/handlers/files.go`)
- **File browser UI:** Tree browser with nested folders (expand/collapse),
type icons, size display, on-hover delete button. Replaces the flat file list
from v0.37.16. (`src/js/sw/surfaces/projects/detail.js`)
- **File action bar:** "New folder" and "Download all" buttons above the file
tree when files exist. (`detail.js`)
- **Upload improvements:** Progress counter ("Uploading 3 of 7…"), archive
detection (`.zip`/`.tar.gz` → confirm extract dialog), specific error
messages (413 = quota exceeded). (`detail.js`)
- **SDK methods:** `deleteFile`, `mkdir`, `uploadArchive` added to
`sw.api.projects` domain. `files()` and `uploadFile()` updated to support
path param. (`src/js/sw/sdk/api-domains.js`)
- **File browser CSS:** 120 lines — file-row, file-chevron, file-icon,
file-name, file-meta, file-delete, dropzone, file-actions-bar.
(`src/css/sw-projects-surface.css`)
- **DESIGN-EXT-CONNECTIONS-LIBRARIES.md:** Extension connections (scoped
credentials) and library packages (shared code + data via `lib.load()`).
Three implementation phases. (`docs/`)
- **Extension track v0.39.x:** Roadmap updated — v0.39.0 multi-file Starlark,
v0.39.1 connections, v0.39.2 libraries, v0.39.3 full composition.
(`docs/ROADMAP.md`)
### Changed
- **Project file list response:** Now returns `WorkspaceFile` objects (with
`path`, `is_directory`, `content_type`, `size_bytes`) instead of `File` model
objects. `GET /projects/:id/files` supports `?path=` and `?recursive=` params.
- **FileHandler constructor:** Gains `SetWorkspaceFS()` method for workspace
injection. Project file routes use dedicated `projFileH` instance with wfs
attached. (`server/main.go`)
- **ICD updated:** `docs/ICD/projects.md` — expanded Project Files section with
all 7 endpoints, request/response schemas, error codes.
- **OpenAPI updated:** `server/static/openapi.yaml` — 5 new paths for project
file operations (download, delete, mkdir, archive upload/download).
### Known Limitations
- **User default workspace:** Not yet implemented. Personal "My Files" deferred
to v0.37.18.
- **Tool output persistence:** `origin=tool_output` auto-save for LLM-generated
artifacts deferred to v0.37.18.
- **Save to workspace bridge:** "Save" action to promote ephemeral chat files
to workspace storage deferred to v0.37.18.
- **Workspace unique constraint:** Race guard on concurrent workspace creation
relies on DB error handling (re-read on duplicate key). Migration 014 for
explicit `UNIQUE(owner_type, owner_id)` not yet added.
---
## [0.37.16.0] — 2026-03-24
### Summary
Projects Surface. Card-grid list + two-column detail view with inline ChatPane,
context menu (color/archive/delete), sidebar sections (description, instructions,
KBs, notes, files), star toggle, deep-linking, and search/sort. UserMenu in header
on both views. Backend was already complete (16 protected + 2 admin endpoints,
18 SDK methods); this changeset delivers the full Preact frontend.
### Added
- **Projects list view:** Card grid with search filter, activity/name sort,
"+ New project" via `sw.prompt` dialog, UserMenu in header left-of-title.
(`src/js/sw/surfaces/projects/list.js`, `src/css/sw-projects-surface.css`)
- **Projects detail view:** Two-column layout — left panel for conversations
(inline ChatPane, "+ New" channel creation, channel list with back nav),
right sidebar for description/instructions textareas with save, collapsible
Knowledge Bases/Notes/Files sections with "+" picker dropdowns.
(`src/js/sw/surfaces/projects/detail.js`)
- **Context menu:** "..." button opens dropdown with Rename, 8 color swatches,
Archive/Unarchive toggle, Delete with confirm dialog. All actions persist
via `PUT /api/v1/projects/:id`. (`detail.js`)
- **Star toggle:** Optimistic ☆↔★ toggle, persists in `settings.starred`.
(`detail.js`)
- **Deep-linking:** `/projects/:id` loads detail view directly via
`window.__PROJECT_ID__` template injection. Invalid IDs gracefully fall
back to list view. (`src/js/sw/surfaces/projects/index.js`,
`server/pages/loaders.go`, `server/pages/templates/surfaces/projects.html`)
- **Surface registration:** `/projects` route with `:id` param, nginx proxy,
docker-entrypoint FE route, conditional CSS/JS loading in `base.html`.
(`server/pages/pages.go`, `nginx.conf`, `docker-entrypoint-fe.sh`,
`server/pages/templates/base.html`)
### Fixed
- **Picker section expand:** `openPicker()` now auto-expands the collapsed
section when "+" is clicked, so the picker dropdown is visible without
manually toggling the chevron first. (`detail.js`)
### Changed
- **UserMenu in header (list view):** Moved from bottom footer to header row,
left of "Projects" title. Consistent with detail view placement. Removed
`.sw-projects__list-footer` CSS class. (`list.js`, `sw-projects-surface.css`)
### Known Limitations
- **File upload:** UI is wired (dropzone + click handler) but backend returns
"failed to save file metadata" — requires workspace storage layer. File
upload (archive upload/download) is required for MVP; deferred to v0.37.17
(Workspaces). Git integration is optional/post-MVP.
- **Conversation names:** Channel list shows raw UUID instead of friendly name
(channels created as type `direct` have no name field).
---
## [0.37.15.0] — 2026-03-23
### Summary
Workflow Ownership & Lifecycle (CS1–CS5). Adds instance cancel with assignment
cascade, assignment unclaim/reassign/cancel operations, full FE API domain
wiring for stage CRUD and assignments, team-admin workflows rewrite with
tabbed layout (Workflows + Stage Editor | Assignments Queue | Instance Monitor),
and settings assignments view replacing the read-only workflow list.
### Added
- **Instance cancel:** `POST /channels/:id/workflow/cancel` sets
`workflow_status='cancelled'` and cascades to all open assignments. Auth:
instance owner, team admin, or global admin. Team-scoped variant at
`POST /teams/:teamId/workflows/monitor/instances/:channelId/cancel`.
(`server/handlers/workflow_instances.go`, `server/store/*/channel.go`)
- **Assignment unclaim:** `POST /workflow-assignments/:id/unclaim` returns
claimed assignment to unassigned. Auth: claimer or team admin.
(`server/handlers/workflow_assignments.go`, `server/store/*/workflows.go`)
- **Assignment reassign:** `POST /workflow-assignments/:id/reassign` changes
`assigned_to` on claimed assignment. Auth: team admin.
(`server/handlers/workflow_assignments.go`, `server/store/*/workflows.go`)
- **Assignment cancel:** `POST /workflow-assignments/:id/cancel` cancels a
single assignment. Auth: team admin.
(`server/handlers/workflow_assignments.go`, `server/store/*/workflows.go`)
- **FE API domains:** `workflowAssignments` domain (mine, claim, unclaim,
complete, reassign, cancel, comment). Team domain additions: assignments,
workflowInstances, cancelWorkflowInstance, stage CRUD (workflowStages,
createWorkflowStage, updateWorkflowStage, deleteWorkflowStage,
reorderWorkflowStages). (`src/js/sw/sdk/api-domains.js`)
- **Team-admin workflows tabs:** Tabbed layout (Workflows | Assignments |
Monitor). Workflows tab includes inline stage editor with add/edit/delete.
Assignments tab shows claimed + available queue with claim/release/complete
actions and WS live updates. Monitor tab shows active instances with cancel.
(`src/js/sw/surfaces/team-admin/workflows.js`)
- **Settings assignments:** Replaces read-only workflow list with personal
assignment queue across all teams. Shows claimed and available assignments
with claim/release/open/complete actions.
(`src/js/sw/surfaces/settings/workflows.js`)
### Changed
- Settings sidebar label "Workflows" → "Assignments".
(`src/js/sw/surfaces/settings/index.js`)
---
## [0.37.14.23] — 2026-03-23
### Summary
Extension surface user menu fix + roadmap workflow integration. The ICD Runner
(and all extension surfaces) now mount the Preact UserMenu via `sw.userMenu()`
instead of relying on the dead Go template hydration.
### Fixed
- **Extension surface "? User" label:** extension template included static
`{{template "user-menu"}}` which rendered placeholder HTML ("? User") since
the legacy hydration script was removed in v0.37.13. Removed the dead
template inclusion. (`server/pages/templates/surfaces/extension.html`)
- **Extension surface user menu click does nothing:** ICD Runner never called
`sw.userMenu()` to mount the Preact UserMenu component. Added topbar with
`sw.userMenu(topbar, { flyout: 'down' })` in `renderShell`. Also uses
`sw.auth.user` instead of dead `window.__USER__` for header display.
(`packages/icd-test-runner/js/ui.js`, `packages/icd-test-runner/js/main.js`)
### Changed
- **Roadmap:** added v0.37.15 reference to `DESIGN-0.37.15.md` workflow design
doc, added v0.38.x Workflow Product Maturity section referencing
`ROADMAP-0.38.md`. Trimmed completed version descriptions.
---
## [0.37.14.22] — 2026-03-23
### Summary
Thinking tags persistence + deleteMessage endpoint. Two features that close
out the v0.37.14 pane audit. Also fixes an S9 latent bug (messages paginated
response not unwrapped) and a SQLite NULL scan crash in GetByID.
### Added
- **deleteMessage server endpoint** — `DELETE /channels/:id/messages/:msgId`
with soft-delete, channel access check, and `message.deleted` WS broadcast
to other participants. (`server/handlers/messages.go`, `server/main.go`)
- **`message.deleted` event type** — registered as `DirToClient` for
cross-tab message removal. (`server/events/types.go`)
- **`deleteMessage` SDK method** — `rc.del(...)` in channels namespace.
(`src/js/sw/sdk/api-domains.js`)
- **`message.deleted` WS handler** — filters by active channel, removes
message from state. (`src/js/sw/components/chat-pane/use-chat.js`)
- **`onDelete` wired in ChatPane** — delete button now visible on all
messages. (`src/js/sw/components/chat-pane/index.js`)
- **`extractThinking` / `hydrateThinking` helpers** — parse `` blocks
from message content on the load path (switchChannel, loadMore, WS
message.created). (`src/js/sw/components/chat-pane/use-chat.js`)
### Fixed
- **Thinking tags lost on refresh:** `` content rendered correctly
during streaming (via SSE `reasoning_content`) but spilled as raw text
after page reload. Now extracted on the load path and rendered in the
collapsible `` block. Frontend-only fix, no DB migration.
- **S9 messages unwrap bug:** messages endpoint returns paginated response
with 5 sibling keys, so `_unwrap` preserves the full object. `resp || []`
produced an object, not an array. Changed to `resp.data || resp || []`.
- **SQLite NULL scan crash:** `GetByID` fails on user messages where `model`
and `tokens_used` are NULL (struct uses `string`/`int`, not pointers).
Delete handler now uses a direct `EXISTS` query instead.
---
## [0.37.14.21] — 2026-03-23
### Summary
Double-unwrap cleanup + API envelope normalization. Eliminated 93 defensive
`resp.data || resp || []` patterns across the frontend by fixing `_unwrap` to
preserve sibling fields and normalizing all Go handlers to use `{data: [...]}`
standard envelopes. Three latent bugs fixed (roles, provider types, audit).
### Changed
- `src/js/sw/sdk/rest-client.js` — `_unwrap` now only strips `{data: [...]}` when
`data` is the sole key (`Object.keys(json).length === 1`). Responses with siblings
(e.g. `{data: [...], total: N}`) pass through intact.
- **15 Go handlers** normalized from named keys (`users`, `configs`, `models`,
`channels`, `participants`, `tools`, `folders`, `messages`, `values`, `packages`)
to standard `{data: [...]}` envelope:
- `server/handlers/admin.go` — ListUsers, ListGlobalConfigs, ListModelConfigs,
ListArchivedChannels
- `server/handlers/apiconfigs.go` — ListConfigs, ListModels
- `server/handlers/channel_models.go` — List, Add, Update, Delete
- `server/handlers/participants.go` — List, Add, Update, Remove
- `server/handlers/presence.go` — SearchUsers
- `server/handlers/completion.go` — ListTools
- `server/handlers/folders.go` — List
- `server/handlers/notes.go` — ListFolders
- `server/handlers/messages.go` — GetActivePath, SetCursor
- `server/handlers/teams.go` — ListAvailableModels
- `server/handlers/team_providers.go` — ListTeamProviderModels
- `server/handlers/packages.go` — UpdatePackageSettings
- `server/handlers/package_registry.go` — BrowseRegistry
- `server/handlers/roles.go` — ListRoles (bare map → wrapped in `{data: map}`)
- **~45 frontend files** — simplified all double-unwrap patterns:
- Sole-key endpoints (unwrapped): `resp || []`
- Sibling endpoints (not unwrapped): `resp.data || []`, `resp.total || 0`
- Roles map: `r.data || {}` + `Object.entries()` for iteration
- Go integration + live provider tests updated for new envelope keys
- Frontend contract + policy-gating tests updated
### Fixed
- **Admin → Roles always empty:** `ListRoles` returned a raw map; `r.roles`
was undefined on it. Now wrapped in `{data: map}`, frontend iterates via
`Object.entries(roles)`.
- **Admin → Provider Types dropdown always empty:** `GetProviderTypes` returned
`{data: types}`, `_unwrap` stripped it to bare array, `t.types` was undefined.
Now correctly uses `t || []`.
- **Admin → Audit entries always empty:** `_unwrap` stripped `{data, total}` to
just the array, losing `total`. Now preserved because of sibling-aware `_unwrap`.
### Design Notes
- **Envelope contract:** All list endpoints now return `{data: [...]}` or
`{data: [...], ...metadata}`. No more `{users: [...]}`, `{models: [...]}`, etc.
- **`_unwrap` contract:** sole-key `{data: [...]}` → bare array;
sibling keys present → full object (frontend accesses `.data` and siblings).
- **Editor package skipped:** `packages/editor/js/main.js` uses `API._get` which
bypasses `_unwrap`; defensive patterns intentionally preserved.
---
## [0.37.14] — 2026-03-22
### Summary
Scorched Earth IV + Pane Audit — fourth dead-code purge. 4 files deleted (~−1,672 lines).
The old global layer (`sb.js`, `events.js`, `switchboard-sdk.js`, `workflow-surfaces.js`)
is fully removed. Two survivors (`debug.js`, `repl.js`) cleaned of all old-layer dependencies
and now stand alone with direct `window.*` exports.
### Removed
- `src/js/sb.js` (133 LOC) — global action registry (`window.sb`); onclick handlers
in base.html rewritten to direct function calls
- `src/js/events.js` (361 LOC) — event bus + WebSocket bridge (`window.Events`);
WebSocket auth called deleted `API` global; new SDK (`sw/sdk/events.js`) is the
sole event system
- `src/js/switchboard-sdk.js` (~697 LOC) — composition layer (`window.Switchboard`);
`window.sw` was immediately overwritten by Preact SDK `boot()`; most methods
referenced deleted globals (`API`, `UI`)
- `src/js/workflow-surfaces.js` (481 LOC) — workflow stage surface registry; loaded
deleted `ui-primitives.js` as dependency (already broken at runtime since v0.37.13)
- `base.html`: removed 3 script tags, removed `Switchboard.init()` block
(including dead `handleLogout` referencing deleted `Events`/`API`)
### Changed
- `src/js/debug.js` — replaced `sb.ns()`/`sb.register()` with direct `window.*`
exports; replaced dead `API`/`App`/`Events`/`Extensions` state snapshot with
Preact SDK `window.sw` introspection; diagnostics use `sw.auth.token` and
`sw.events.connected`; fixed `copyDebugLog` referencing deleted `UI.toast`
- `src/js/repl.js` — replaced `sb.ns()` with `window.REPL`; admin gate uses
`window.sw.auth.user.role` instead of deleted `API.user.role`; injected globals
reduced to `sw` + `DebugLog`; removed dead event label tab-completion
- `server/pages/templates/base.html` — 11 `sb.call()` onclick handlers rewritten
to direct function calls
- `server/pages/templates/workflow.html` — broken custom surface loader replaced
with graceful error message
- `src/sw.js` — removed 3 entries from SHELL_FILES (sb.js, events.js,
switchboard-sdk.js)
- `packages/icd-test-runner/js/tier-sdk.js` — boot tests updated from
`Switchboard.init()` to Preact SDK `boot()`; identity tests use `sw.auth.user`;
theme test uses `sw.emit()`; removed extension compat shim test
- `src/js/sw/sdk/index.js` — SDK version marker updated to 0.37.14
### Design Notes
- **2 old JS files survive:** `debug.js` (debug modal + diagnostics) and `repl.js`
(admin REPL console). Both are standalone with zero old-layer dependencies.
Scheduled for Preact rewrite at v0.37.17.
- **Cumulative scorched earth:** 53 files deleted, ~−19,382 lines across four
passes (v0.37.10, v0.37.12, v0.37.13, v0.37.14).
---
## [0.37.13] — 2026-03-22
### Summary
Scorched Earth III — third dead-code purge. 5 files deleted (−2,269 net lines).
The massive `ui-primitives.js` (1,133 LOC) is gone; its few remaining consumers
(`debug.js`, `switchboard-sdk.js`) received inlined replacements. Theme management
migrated from the global `Theme` object to the Preact SDK's `createTheme` module.
SDK slimmed by removing 6 dead factory wrappers.
### Removed
- `src/js/ui-primitives.js` (1,133 LOC) — `esc()`, `createComponentRegistry()`,
`componentMixin()`, `Providers`, `Roles`, `showConfirm()`, `showPrompt()`,
`openModal()`, `closeModal()`, `Theme`, render helpers. All consumers deleted
or inlined.
- `src/js/code-editor.js` (362 LOC) — tabbed CodeMirror 6 editor, zero consumers
- `src/js/file-tree.js` (290 LOC) — workspace file browser widget, zero consumers
- `src/js/user-menu.js` (206 LOC) — vanilla flyout menu; all surfaces use
Preact `sw/shell/user-menu.js`
- `src/js/app-state.js` (139 LOC) — global `window.App` state; Preact surfaces
use local state hooks; `debug.js` state snapshot already guards with `typeof`
### Changed
- `src/js/debug.js` — inlined `openModal()`, `closeModal()`, `showConfirm()`,
`_escHtml()` (~44 LOC) to replace dependency on deleted `ui-primitives.js`
- `src/js/switchboard-sdk.js` — removed 6 dead factory wrappers (`sw.notes()`,
`sw.panels()`, `sw.fileTree()`, `sw.codeEditor()`, `sw.layout()`,
`sw.userMenu()` + `_hydrateUserMenu()`); replaced global `Theme` with
`import { createTheme } from './sw/sdk/theme.js'`; inlined `esc()` and
`openModal`/`closeModal` helpers; net −213 LOC
- `server/pages/templates/base.html` — removed 5 script tags (`app-state.js`,
`ui-primitives.js`, `user-menu.js`, `file-tree.js`, `code-editor.js`);
consolidated removal comments
- `src/sw.js` — removed 5 entries from `SHELL_FILES` cache manifest
### Design Notes
- **6 old JS files survive:** `sb.js` (debug modal onclick handlers),
`events.js` (extension bridge), `switchboard-sdk.js` (extension compatibility),
`debug.js` + `repl.js` (active tooling), `workflow-surfaces.js` (deferred to
Preact rewrite). Targeted for removal as extensions migrate.
- **`user-menu.css` kept:** extension surface template still renders server-side
user-menu HTML; CSS provides styling for the placeholder.
- **Cumulative scorched earth:** 49 files deleted, ~−17,710 lines across three
passes (v0.37.10, v0.37.12, v0.37.13).
---
## [0.37.12] — 2026-03-22
### Summary
Scorched Earth II — second dead-code purge. 23 files deleted (~2,600 lines).
All six core surfaces are confirmed Preact-only; old vanilla JS, CSS, and
admin templates removed. Service worker cache manifest overhauled.
### Removed
- `src/js/ui-format.js` — old markdown renderer (replaced by `sw/components/chat-pane/markdown.js`)
- `src/js/virtual-scroll.js` — only consumer was deleted `chat.js`
- `src/js/model-selector.js` — Preact ChatPane has its own `ModelSelector`
- `src/js/drag-resize.js` — only consumer was deleted `PaneContainer`
- `src/js/pages-splash.js` — login is Preact since v0.37.5
- `src/js/task-sidebar.js` — no template loads this
- `src/js/workflow-api.js` — new SDK has `sw.api.workflows.*`
- `src/js/workflow-queue.js` — references deleted `App.chats`
- `src/js/workflow-monitor.js` — ES module export, never imported
- `src/css/splash.css` — replaced by `sw-login.css`
- `src/css/styles.css` — 13-line decomposition comment from v0.22.9
- `src/css/persona-kb.css` — old admin/settings UI
- `src/css/notification-prefs.css` — old settings UI
- `src/css/memory.css` — old admin/settings UI
- `src/css/channel-models.css` — old chat autocomplete
- `server/pages/templates/admin/` — 8 dead templates (overview, users,
models, providers, settings, teams, roles, routing); admin is Preact
since v0.37.6
### Changed
- `server/pages/templates/base.html` — removed 7 script/link tags:
`splash.css`, `marked.min.js`, `purify.min.js`, `ui-format.js`,
`virtual-scroll.js`, `model-selector.js`, `drag-resize.js`.
`marked`/`purify` now loaded only by surfaces that need them (chat, notes).
- `src/sw.js` — replaced entire `SHELL_FILES` array; removed 17 stale
entries referencing files deleted in v0.37.10, added all `sw-*.css` files
- `server/pages/pages.go` — removed `templates/admin/*.html` from
`//go:embed` directive and `ParseFS` call
### Design Notes
- **11 old JS files survive:** `sb.js`, `app-state.js`, `events.js`,
`ui-primitives.js`, `user-menu.js`, `file-tree.js`, `code-editor.js`,
`switchboard-sdk.js`, `debug.js`, `repl.js`, `workflow-surfaces.js`.
These provide globals consumed by extension surfaces (`Events.on()`,
`Theme.resolved()`, `sw.fileTree()`) and the debug modal (`sb.call()`).
Targeted for removal when extensions migrate to Preact SDK.
- **`workflow-surfaces.js` deferred:** still loaded by `workflow.html`
standalone page; removed when workflow surfaces rebuilt (v0.37.13).
---
## [0.37.11] — 2026-03-22
### Summary
Notes surface built as full Preact surface, replacing the v0.37.9 template
mount. Sidebar with folder tree, tag cloud, search, and UserMenu. NotesPane
runs `standalone=false` — the surface sidebar owns filter controls. Cross-surface
fixes: Menu primitive CSS was never loaded (now added to base.html), Avatar
initials fixed for single-word names.
### New
- `src/js/sw/surfaces/notes/index.js` — NotesSurface root, self-mounts at `#notes-mount`
- `src/js/sw/surfaces/notes/use-workspace.js` — sidebar toggle + active note (sessionStorage)
- `src/js/sw/surfaces/notes/use-sidebar.js` — folders, tags, search, WS live updates
- `src/js/sw/surfaces/notes/sidebar.js` — search input, folder/tag sections, UserMenu footer
- `src/js/sw/surfaces/notes/sidebar-folders.js` — collapsible folder tree with counts
- `src/js/sw/surfaces/notes/sidebar-tags.js` — collapsible tag cloud with active state
- `src/js/sw/surfaces/notes/notes-workspace.js` — workspace header + NotesPane
- `src/css/sw-notes-surface.css` — surface layout (sidebar + workspace + mobile responsive)
### Changed
- `src/js/sw/components/notes-pane/index.js` — handleRef expanded with `setFolder`,
`setTagFilter`, `setSearchQuery`, `getFolder`, `getTagFilter`; threads `standalone`
prop to NoteList
- `src/js/sw/components/notes-pane/note-list.js` — `standalone=false` hides search row,
folder dropdown, tag cloud (surface sidebar owns these)
- `src/js/sw/primitives/avatar.js` — single-word names show first letter only
("admin" → "A" not "AD"); multi-word unchanged ("John Doe" → "JD")
- `server/pages/templates/surfaces/notes.html` — full rewrite: crash catcher +
`#notes-mount` + Preact boot (mirrors chat.html pattern)
- `server/pages/templates/base.html` — added `sw-primitives.css` link (Menu primitive
was unstyled on all surfaces); added `css-notes` conditional
- `src/css/surfaces.css` — deleted `.surface-notes*` rules (9 lines, replaced by
`sw-notes-surface.css`)
### Design Notes
- **Sidebar ↔ NotesPane coordination:** write-only via handleRef. Sidebar sets
folder/tag/search on the kit; kit reloads its own list. No bidirectional sync
needed because `standalone=false` hides the kit's own filter controls.
- **Menu primitive bug:** `sw-primitives.css` was never loaded in `base.html`.
The Menu component (`sw-menu__item`) had no hover, no cursor, no background.
Chat surface masked this by using its own `.sw-chat-surface__context-menu`
styles. Fixed globally — all surfaces now get proper Menu styles.
- **Kit enhancements deferred:** markdown toolbar, wikilink hover preview,
breadcrumbs, split view, templates, slash commands, graph minimap all moved
to v0.37.14 pane audit.
### Cumulative Layer Status
| Layer | Version | Status |
|-------|---------|--------|
| Preact+htm vendor | v0.37.2 | ✅ |
| Layer 0: Primitives | v0.37.11 | ✅ (Menu CSS fix, Avatar fix) |
| Layer 1: SDK | v0.37.10 | ✅ (8 modules) |
| Layer 1.5: ChatPane | v0.37.10 | ✅ (13 files) |
| Layer 1.5: NotesPane | v0.37.11 | ✅ (10 files, standalone prop) |
| Layer 2: Shell | v0.37.4 | ✅ (5 components) |
| Surface: Login | v0.37.5 | ✅ |
| Surface: Settings | v0.37.7 | ✅ |
| Surface: Admin | v0.37.6 | ✅ |
| Surface: Team Admin | v0.37.7 | ✅ |
| Surface: Chat | v0.37.10 | ✅ |
| Surface: Notes | v0.37.11 | ✅ |
## [0.37.10] — 2026-03-21
### Summary
Scorched earth rebuild of the Chat surface. The old 16-file imperative SPA
(~11,500 lines of `app.js`, `chat.js`, `ui-core.js`, `projects-ui.js`, etc.)
is replaced by a focused Preact surface (8 files) composing the ChatPane kit.
ChatPane kit upgraded with error display, stop generation, regenerate, code
block copy, markdown toolbar, emoji picker, and message pagination. Features
that don't belong on the chat surface (projects, KB management, extensions,
notifications) are deferred — each earns its way back as its own surface or
shell-level feature.
32 files changed. +774 / −13,615. **Net: −12,841 lines.**
### New
- **Chat Surface** (`src/js/sw/surfaces/chat/`) — 8 files:
- `ChatSurface` root — sidebar + workspace + ToastContainer + DialogStack
- `useWorkspace` — active conversation coordinator, sessionStorage restore
- `useSidebar` — channels, chats, folders data; search filter; WS live
updates; CRUD (rename, delete, move-to-folder, create folder)
- `Sidebar` — search bar, "New Chat" button, collapsible Channels section,
collapsible Chats section with folder grouping, context menus, desktop
collapse toggle
- `SidebarChannels` — DMs + named channels, unread badges
- `SidebarChats` — personal AI chats, folder grouping, context menus
(rename, delete, move to folder)
- `ChatWorkspace` — workspace header (sidebar toggle, tools button) +
ChatPane in `standalone=false` mode (surface manages navigation)
- `ToolsPopup` — scrollable category-based tool toggles with persistence,
`useTools` hook loads from API
- **ChatPane Kit enhancements** (4 new components in `chat-pane/`):
- `CodeBlock` — language badge + copy button, code block extraction from
rendered markdown HTML
- `MessageActions` — per-message toolbar: regenerate, copy text, delete.
Appears on hover/focus
- `MarkdownToolbar` — bold, italic, code, code block, link, bullet list,
numbered list, heading, blockquote. Wraps selection with markdown syntax.
Keyboard shortcuts: Ctrl+B, Ctrl+I, Ctrl+E
- `EmojiPicker` — categorized grid (7 categories), search, recently used
(localStorage). Inserts at cursor position
- **ChatPane Kit upgrades** (6 modified files):
- `index.js` — error banner rendered from `error` state, stop button wiring,
new kit re-exports
- `use-chat.js` — `regenerate(msgId)`, `deleteMessage(msgId)`, `loadMore()`
pagination, `hasMore`/`loadingMore` state
- `message-list.js` — "Load older messages" pagination button, scroll
position preservation on prepend, ARIA `role="log"`, regenerate/delete
wiring per message
- `message-bubble.js` — CodeBlock rendering with copy+language badge,
MessageActions toolbar (regenerate/copy/delete), relative timestamps
- `message-input.js` — MarkdownToolbar, EmojiPicker, stop button (replaces
send during streaming), ARIA labels, keyboard format shortcuts
- `markdown.js` — `marked.setOptions()` with GFM+breaks, task list checkbox
rendering via custom renderer
- **UserMenu auto-surface-filtering** — reads `body[data-surface]`, auto-
includes links to all other surfaces (Notes, extensions), filters out the
current surface. Default `handleSelect` navigates via `location.href`.
Every surface gets the same menu minus itself.
- **`sw-chat-surface.css`** — sidebar + workspace flex layout, responsive
mobile overlay, sidebar collapse, tools popup, context menus (~600 lines)
### Deleted
**16 JS files (~11,500 lines):**
- `src/js/app.js` (589) — SPA bootstrap, sidebar, init
- `src/js/api.js` (995) — REST client, auth, token management
- `src/js/chat.js` (1,344) — message rendering, streaming, CM6 input
- `src/js/chat-pane.js` (160) — legacy ChatPane wrapper
- `src/js/channel-models.js` (437) — channel model management UI
- `src/js/extensions.js` (506) — extension panel, package management
- `src/js/files.js` (857) — file upload/staging UI
- `src/js/knowledge-ui.js` (556) — knowledge base toggle popup
- `src/js/notifications.js` (431) — notification bell + dropdown
- `src/js/pages.js` (349) — admin page rendering
- `src/js/pane-container.js` (589) — split pane resize manager
- `src/js/panels.js` (454) — side panel registration + lifecycle
- `src/js/projects-ui.js` (2,090) — project sidebar section
- `src/js/tokens.js` (167) — token count display
- `src/js/tools-toggle.js` (245) — tools toggle popup
- `src/js/ui-core.js` (1,699) — core UI: sidebar, messages, context menus
**5 CSS files (~1,650 lines):**
- `src/css/chat.css` (648) — old chat area, sidebar, workspace, input
- `src/css/chat-pane.css` — legacy chat pane styles
- `src/css/panels.css` — side panel styles
- `src/css/pane-container.css` — split pane layout
- `src/css/notifications.css` — notification bell + dropdown
### Changed
- `chat.html` — rewritten from ~387-line SPA scaffold to ~50-line Preact boot
(same pattern as settings/admin/team-admin surfaces)
- `base.html` — removed `