All checks were successful
CI/CD / detect-changes (pull_request) Successful in 5s
CI/CD / test-runners (pull_request) Has been skipped
CI/CD / e2e-smoke (pull_request) Has been skipped
CI/CD / test-frontend (pull_request) Successful in 6s
CI/CD / test-go-pg (pull_request) Successful in 2m42s
CI/CD / test-sqlite (pull_request) Successful in 2m58s
CI/CD / build-and-deploy (pull_request) Successful in 1m33s
Manifest declarations for slots (host surfaces declare injection points) and contributes (extensions declare UI contributions). lib.require() relaxed from library-only to any package with exports. Admin slots aggregation endpoint. SDK renderAll() helper. 7 new tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2527 lines
125 KiB
Markdown
2527 lines
125 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to Armature are documented here.
|
||
|
||
## v0.8.5 — Extension Composability
|
||
|
||
Extensions can now compose with each other through declared slots, UI
|
||
contributions, and cross-package function calls. This is the last kernel
|
||
feature before 1.0 — the platform now supports the "extensions extending
|
||
extensions" pattern.
|
||
|
||
**Manifest declarations**
|
||
|
||
- Surfaces declare named `slots` in their manifest (e.g., `toolbar-actions`,
|
||
`note-footer`) with context documentation for extension authors.
|
||
- Extensions declare `contributes` entries targeting `{host}:{slot}` names
|
||
(e.g., `notes:toolbar-actions`). Coupling is soft — install order doesn't
|
||
matter.
|
||
- The kernel validates slot/contribution conventions at install time.
|
||
|
||
**Backend: `lib.require()` relaxation**
|
||
|
||
- `lib.require()` now works with any package that declares `exports`, not
|
||
just `type: "library"` packages. A full package (with surfaces, settings,
|
||
UI) can export callable functions for cross-package use.
|
||
- The existing `depends` and permission model is unchanged — the called
|
||
function runs with the target package's permissions.
|
||
|
||
**Admin slots endpoint**
|
||
|
||
- `GET /api/v1/admin/slots` returns an aggregated map of all declared slots
|
||
across installed packages with their contributors.
|
||
- Uninstalling a package that declares slots warns about orphaned
|
||
contributions in other packages.
|
||
|
||
**SDK additions**
|
||
|
||
- `sw.slots.renderAll(name, context)` — convenience helper for host surfaces
|
||
to render all components in a slot with error isolation.
|
||
- `sw.slots.declare(name, description)` — runtime slot declaration for
|
||
discoverability and debugging.
|
||
|
||
**Documentation**
|
||
|
||
- `PACKAGE-FORMAT.md` — added `slots`, `contributes`, `depends` field docs.
|
||
- `EXTENSION-GUIDE.md` — new "Extension Composability" section with slot
|
||
naming conventions, contribution patterns, and cross-package call examples.
|
||
- `STARLARK-REFERENCE.md` — updated `lib` module to reflect exports-based
|
||
calling (not library-type-only).
|
||
|
||
**Modified files:**
|
||
|
||
- `server/sandbox/lib_module.go` — type check → exports check
|
||
- `server/handlers/extensions.go` — composability field validation
|
||
- `server/handlers/packages.go` — orphaned contribution warning
|
||
- `server/main.go` — admin slots route registration
|
||
- `src/js/sw/sdk/slots.js` — `renderAll()`, `declare()`, `declarations()`
|
||
- `docs/PACKAGE-FORMAT.md` — slots, contributes, depends
|
||
- `docs/EXTENSION-GUIDE.md` — composability section
|
||
- `docs/STARLARK-REFERENCE.md` — lib module update
|
||
- `docs/DESIGN-extension-composability.md` — status Draft → Implemented
|
||
|
||
**New files:**
|
||
|
||
- `server/handlers/admin_slots.go` — admin slot aggregation endpoint
|
||
|
||
## v0.8.4 — Documentation Refresh + Surface Sizing Fix
|
||
|
||
Eight versions of module additions (v0.7.5–v0.8.3) shipped without a
|
||
docs pass. This release brings the public-facing guides up to date and
|
||
fixes a CSS layout bug affecting all surfaces.
|
||
|
||
**Documentation refresh**
|
||
|
||
- `STARLARK-REFERENCE.md` — added `workspace` module section (5 builtins),
|
||
`permissions` module section, and `settings.has_capability()` documentation.
|
||
- `EXTENSION-GUIDE.md` — added `capabilities` manifest block, `vector(N)`
|
||
column type, `user_permissions` and `gate_permission` manifest fields,
|
||
updated sandbox permissions list with v0.8.0+ additions (`files.read`,
|
||
`files.write`, `workspace.manage`).
|
||
- `TUTORIAL-FIRST-EXTENSION.md` — reviewed for v0.7+ accuracy (no changes needed).
|
||
|
||
**Surface sizing fix**
|
||
|
||
All surfaces using the shell topbar had scroll content clipped at the
|
||
bottom by ~44px (the topbar height). Root cause: surface containers were
|
||
siblings of `#shell-topbar` inside `.surface-inner`, which used
|
||
`height: 100%` without flex layout — the surface div claimed the full
|
||
parent height, ignoring the topbar sibling.
|
||
|
||
- Fix: `.surface-inner` now uses `display: flex; flex-direction: column`
|
||
so the topbar and surface share vertical space via flex layout.
|
||
- All surface containers (`.surface-docs`, `.surface-admin`,
|
||
`.surface-settings`, `.surface-editor`, `.extension-surface`) changed
|
||
from `height: 100%` to `flex: 1; min-height: 0`.
|
||
- Inline styles on `surface-team-admin` and `welcome-mount` templates
|
||
updated to match.
|
||
|
||
**Modified files:**
|
||
|
||
- `server/pages/templates/base.html` — `.surface-inner` gains flex column layout
|
||
- `src/css/surfaces.css` — `.surface-docs`, `.surface-admin`, `.surface-settings`, `.surface-editor`
|
||
- `src/css/extension-surface.css` — `.extension-surface`
|
||
- `server/pages/templates/surfaces/team-admin.html` — inline style fix
|
||
- `server/pages/templates/surfaces/welcome.html` — inline style fix
|
||
- `docs/STARLARK-REFERENCE.md` — workspace, permissions, has_capability
|
||
- `docs/EXTENSION-GUIDE.md` — capabilities, vector, user_permissions, gate_permission
|
||
|
||
## v0.8.3 — Vector Column Type
|
||
|
||
Extensions can now declare vector columns and perform similarity search.
|
||
Three-tier progressive enhancement: native pgvector on Postgres, JSONB
|
||
fallback without pgvector, TEXT fallback on SQLite.
|
||
|
||
**Manifest: `db_tables` vector columns**
|
||
|
||
- Declare `"vector(N)"` as a column type (N = dimension, 1–4096).
|
||
- On Postgres with pgvector: native `vector(N)` type with auto-created
|
||
HNSW index (`vector_cosine_ops`).
|
||
- On Postgres without pgvector: `JSONB` column.
|
||
- On SQLite: `TEXT` column (JSON-encoded float arrays).
|
||
|
||
**Starlark API**
|
||
|
||
- `db.query_similar(table, column, vector=[], limit=10, filters={}, metric="cosine")`
|
||
— returns rows ordered by ascending cosine distance with injected `_distance` key.
|
||
- `db.insert()` now accepts list values (serialized as JSON strings) for
|
||
vector column storage.
|
||
|
||
**Internal**
|
||
|
||
- Modified: `handlers/ext_db_schema.go` — `parseVectorDim`, `mapColType`
|
||
gains `hasPgvector` parameter, HNSW index creation for vector columns.
|
||
- Modified: `sandbox/db_module.go` — `HasPgvector` in `DBModuleConfig`,
|
||
list support in `starlarkToGoValue`, `dbQuerySimilar` with pgvector and
|
||
fallback paths, `cosineDistance` helper.
|
||
- Modified: `sandbox/runner.go` — wire `HasPgvector` from capabilities.
|
||
- Modified: `handlers/extensions.go` — `SetCapabilities` on `ExtensionHandler`.
|
||
- Modified: `server/main.go` — wire capabilities to extension handler.
|
||
- Updated: `docs/STARLARK-REFERENCE.md` — vector similarity section.
|
||
- New tests: 5 schema tests + 8 db module tests (13 total).
|
||
|
||
## v0.8.2 — Capability Negotiation
|
||
|
||
Extensions declare environment requirements in their manifest. The kernel
|
||
validates at install time and exposes a runtime query for graceful degradation.
|
||
|
||
**Manifest: `capabilities` block**
|
||
|
||
- `capabilities.required` — array of capability names. Install is rejected
|
||
(HTTP 422) if any are unavailable. Rollback deletes the package row.
|
||
- `capabilities.optional` — array of capability names. Install succeeds
|
||
regardless; extensions query at runtime via `settings.has_capability()`.
|
||
|
||
**Detected capabilities:** `postgres`, `pgvector`, `object_storage`, `s3`,
|
||
`workspace`.
|
||
|
||
**Starlark API**
|
||
|
||
- `settings.has_capability(name)` — returns `True` or `False`. Always
|
||
available (no permission required).
|
||
|
||
**Admin API**
|
||
|
||
- `GET /api/v1/admin/capabilities` — re-probes and returns current state.
|
||
|
||
**Internal**
|
||
|
||
- New: `handlers/capabilities.go` (detection, parsing, validation, admin handler).
|
||
- New: `handlers/capabilities_test.go` (14 tests).
|
||
- New: `sandbox/settings_module_test.go` (4 tests).
|
||
- Modified: `handlers/packages.go` — replaced stub `checkCapabilities` with
|
||
real validation; `SetCapabilities` setter.
|
||
- Modified: `handlers/packages_bundled.go` — bundled packages with unmet
|
||
required capabilities are skipped on startup.
|
||
- Modified: `sandbox/settings_module.go` — `has_capability` builtin.
|
||
- Modified: `sandbox/runner.go` — `capabilities` field + `SetCapabilities` setter.
|
||
- Modified: `server/main.go` — `DetectCapabilities` at startup, wired to
|
||
runner and package handler, admin route registered.
|
||
|
||
---
|
||
|
||
## v0.8.1 — Workspace Module
|
||
|
||
New `workspace` sandbox module. Managed disk directories for extensions
|
||
that need a real filesystem (git, compilers, media tools).
|
||
|
||
**workspace module (permission: `workspace.manage`)**
|
||
|
||
- `workspace.create(name)` — create a workspace directory (idempotent). Returns absolute path. Enforces quota if `WORKSPACE_QUOTA_MB` > 0.
|
||
- `workspace.path(name)` — get the absolute path of an existing workspace. Returns None if not found.
|
||
- `workspace.list()` — list workspace names for this extension.
|
||
- `workspace.delete(name)` — recursively remove a workspace (idempotent).
|
||
- `workspace.usage(name)` — disk usage in bytes (10-second timeout on directory walk).
|
||
|
||
**Configuration**
|
||
|
||
- `WORKSPACE_ROOT` — mount point for extension workspaces (default `/data/workspaces`).
|
||
- `WORKSPACE_QUOTA_MB` — per-extension quota in MB (default `0` = unlimited).
|
||
|
||
**Internal**
|
||
|
||
- New file: `sandbox/workspace_module.go`.
|
||
- New permission constant: `ExtPermWorkspaceManage`.
|
||
- Config: `WorkspaceRoot`, `WorkspaceQuotaMB` fields.
|
||
- Runner wiring: `SetWorkspaceRoot()` setter, `buildModulesWithLibCtx` creates workspace module when permission granted.
|
||
- Startup: `main.go` creates workspace root directory if writable, graceful degradation if not.
|
||
- All directories scoped to `{WORKSPACE_ROOT}/{packageID}/{name}/`.
|
||
- Security: name regex (`^[a-z][a-z0-9_]{0,62}$`), `filepath.Clean` + prefix check, `filepath.EvalSymlinks` for symlink escape detection.
|
||
- 16 new tests (create, path, list, delete, usage, name validation, quota enforcement).
|
||
|
||
## v0.8.0 — Files Module
|
||
|
||
New `files` sandbox module. Bridges the existing ObjectStore (PVC/S3) into
|
||
the Starlark sandbox with extension-scoped key namespacing.
|
||
|
||
**files module (permissions: `files.read`, `files.write`)**
|
||
|
||
- `files.put(name, content, content_type, metadata)` — store a file with optional metadata companion. Accepts string or bytes content. 50 MB default limit (configurable via `EXT_FILES_MAX_SIZE`).
|
||
- `files.get(name)` — read a file. Returns dict with `content` (bytes), `content_type`, `size`, `metadata`. Returns None if not found.
|
||
- `files.meta(name)` — read metadata only (no content transfer).
|
||
- `files.list(prefix, limit)` — list files by prefix. Returns list of dicts. Filters out internal `_meta/` companions.
|
||
- `files.delete(name)` — delete a file and its metadata companion. Idempotent.
|
||
- `files.delete_prefix(prefix)` — delete all files under a prefix.
|
||
- `files.exists(name)` — check existence without reading.
|
||
|
||
**Internal**
|
||
|
||
- New file: `sandbox/files_module.go`.
|
||
- New permission constants: `ExtPermFilesRead`, `ExtPermFilesWrite`.
|
||
- `ObjectStore` interface gains `List(ctx, prefix, limit)` method; implemented for PVC and S3.
|
||
- Runner wiring: `SetObjectStore()` setter, `buildModulesWithLibCtx` creates files module when permission granted.
|
||
- All keys scoped to `ext/{packageID}/`. Metadata stored as companion JSON at `ext/{packageID}/_meta/{name}`.
|
||
- 16 new tests (15 files module + 1 PVC list).
|
||
|
||
## v0.7.12 — Concurrent Execution Primitive
|
||
|
||
New `batch` sandbox module. Enables extensions to parallelize arbitrary
|
||
Starlark callables — including frozen library exports from `lib.require()`.
|
||
|
||
**batch module (permission: `batch.exec`)**
|
||
|
||
- `batch.exec(callables, timeout=10)` — runs up to 8 zero-arg callables concurrently, each in its own `starlark.Thread` with independent step budget. Returns `(results, errors)` tuple with ordered results. Per-branch timeout (1–30s, default 10).
|
||
- Nested `batch.exec()` calls are prohibited (prevents exponential goroutine growth).
|
||
- `lib.require()` not available inside branches — load libraries before the batch call.
|
||
- `print()` output from branches is discarded.
|
||
|
||
**Internal**
|
||
|
||
- New file: `sandbox/batch_module.go`.
|
||
- New permission constant: `ExtPermBatchExec`.
|
||
- Runner wiring: `buildModulesWithLibCtx` creates batch module when permission granted.
|
||
- Design doc: `docs/DESIGN-batch-exec.md`.
|
||
- 12 new tests (parallel ordering, partial failure, timeout, cancellation, cap enforcement, nesting prevention, frozen sharing, permission gating).
|
||
|
||
## v0.7.11 — Query & HTTP Ergonomics
|
||
|
||
Four new Starlark sandbox builtins. No new permissions, no schema changes.
|
||
|
||
**db module (permission: `db.read`)**
|
||
|
||
- `db.count(table, filters={})` — returns integer count of matching rows.
|
||
- `db.aggregate(table, column, op, filters={})` — single-value aggregation. `op` ∈ {count, sum, avg, min, max}. Returns int, float, or None.
|
||
- `db.query_batch(queries)` — execute up to 10 query specs in a single call. Each spec supports the same parameters as `db.query`.
|
||
|
||
**http module (permission: `api.http`)**
|
||
|
||
- `http.batch(requests)` — concurrent HTTP dispatch of up to 10 requests. Individual failures return error response dicts (`status: 0`) rather than aborting the batch.
|
||
|
||
**Internal**
|
||
|
||
- Extracted `buildSelectQuery` helper from `dbQuery` for reuse by `db.query_batch`.
|
||
- 21 new tests (14 db, 7 http).
|
||
|
||
## v0.7.10 — Workflow Handoff + Assignment UI
|
||
|
||
Closes the three UX gaps found during v0.7.9: public→team handoff,
|
||
team inbox, and manual assignment. Also fixes dead system-admin bypass
|
||
in team middleware and enriches assignment API responses.
|
||
|
||
**Public Stage Handoff**
|
||
|
||
- `RenderWorkflow()` detects audience mismatch (team/system stage + unauthenticated visitor) and renders "Submitted Successfully" screen with reference ID instead of showing the team-gated form.
|
||
|
||
**API Response Enrichment**
|
||
|
||
- `ListByTeam` and `ListMine` handlers enrich assignment records with `workflow_name`, `stage_name`, `sla_breached` by joining instance → workflow → version snapshot. Results cached per-request to avoid repeated DB hits.
|
||
- `ListTeamInstances` returns `instanceView` with `workflow_name`, `stage_name`, `age_seconds`, `sla_breached`.
|
||
|
||
**Team Middleware Fix**
|
||
|
||
- `RequireTeamAdmin` / `RequireTeamMember` system-admin bypass was dead code (`c.Get("role")` never set by auth middleware). Fixed to resolve `PermSurfaceAdminAccess` via `resolveAndCachePerms`. Variadic `allStores` parameter preserves backward compatibility.
|
||
|
||
**Team Workflow Inbox**
|
||
|
||
- Enhanced `AssignmentsTab` in team-admin: "My Active" (claimed) and "Available" (unassigned) sections with claim/unclaim/release/work/complete actions, time-ago display, WS live updates.
|
||
- Manual "Assign" button on unassigned rows with team member dropdown. New `POST /api/v1/assignments/:id/assign` endpoint.
|
||
|
||
**Assignment Notifications**
|
||
|
||
- Engine calls `notifyAssignment()` on assignment creation — notifies specific user or all team members via notification system.
|
||
|
||
**SDK Gap Closure**
|
||
|
||
- Added `workflowAssignments` domain (claim/unclaim/complete/cancel/assign/mine)
|
||
- Added `teams.assignments`, `teams.workflowInstances`, `teams.cancelWorkflowInstance`
|
||
- Fixed dead `workflows.cancel` route referencing `/channels/`
|
||
|
||
**E2E Test**
|
||
|
||
- `ci/e2e-workflow-handoff.sh`: public form → team review → claim → complete. Verifies audience mismatch screen, assignment creation, full lifecycle.
|
||
|
||
---
|
||
|
||
## v0.7.9 — Workflow Independence Audit
|
||
|
||
Workflows proven independent of chat and all optional packages. Critical
|
||
rendering bugs fixed, dead chat UI removed, deferred test debt closed.
|
||
|
||
**RenderWorkflow Fix (critical)**
|
||
|
||
- `RenderWorkflow()` was a stub that never loaded instance/stage data — post-start page was broken. Now loads instance by token/ID, resolves current stage, populates all template fields.
|
||
- `WorkflowPageData` fields renamed: `ChannelID` → `EntryToken`, `ChannelTitle` → `WorkflowTitle`, `ChannelDescription` → `WorkflowDescription` (vestigial chat-era names)
|
||
- Stage modes aligned: template uses Go constants (`form`, `review`, `delegated`, `automated`) instead of legacy `form_only`/`form_chat`
|
||
- Dead chat UI removed: chat CSS, split layout, `sendMessage()`, fallback chat branch (~180 lines deleted from `workflow.html`)
|
||
- Landing page mode conditionals updated to match Go constants
|
||
- OpenAPI `stage_mode` enum corrected (4 occurrences)
|
||
|
||
**Bug Fixes (found during verification)**
|
||
|
||
- Entry token resolution: handler passed route param (instance ID) as entry token to JS. Fixed: resolve actual token from `inst.EntryToken` + `?token=` query param.
|
||
- Fieldset submit guard: `submitForm()` checked `FORM_TPL.fields` but not `.fieldsets` — progressive forms silently no-op'd. Fixed: accept either.
|
||
- Stage advance status check: JS checked `result.status === 'advanced'` but API returns `active`/`completed`. Fixed: on 200 OK with `active`, reload to render next stage.
|
||
|
||
**Deferred test coverage (carried from v0.7.6)**
|
||
|
||
- 17 new SQLite store tests: workflow CRUD, stages, instances, lifecycle (advance/complete/cancel/stale), team scope, API tokens, users, groups
|
||
- `InstallPackage` decomposed from 400-line monolith into 7 private methods: `receiveUpload`, `parseAndValidateArchive`, `extractPackageAssets`, `registerPackage`, `applySchemaAndPermissions`, `resolveDependencies`, `checkCapabilities`
|
||
- `ci/e2e-workflow-nochat.sh` — E2E test for workflow lifecycle without chat
|
||
|
||
**Discovered issues (deferred to v0.7.10)**
|
||
|
||
- Public→authenticated stage handoff: visitor sees auth-gated stage form instead of "submitted" screen
|
||
- Team member pickup UI: no surface for claiming workflow instances
|
||
- Assignment flow: no admin UI for manual instance assignment
|
||
|
||
**Tests:** 17 new store tests, 1 new E2E script
|
||
|
||
---
|
||
|
||
## v0.7.8 — Bug Fixes & Admin Gaps
|
||
|
||
- `StartBySlug` handler + `/api/v1/workflow-entry/:scope/:slug` route for landing page Start button
|
||
- Workflow delete guard: admin endpoint rejects team-scoped workflows (403)
|
||
- Package button cleanup: Delete hidden for bundled packages
|
||
- Package export: `fetch()` with auth token instead of `window.open()`
|
||
- Settings CSS: bottom padding fix for save button cutoff
|
||
- Shared `StageForm` component between admin and team-admin; public entry URL with copy button
|
||
|
||
---
|
||
|
||
## v0.7.7 — API Tokens + Extension Permissions
|
||
|
||
Personal access tokens (PATs) for programmatic API access, plus extension-declared
|
||
user permissions for backend RBAC enforcement.
|
||
|
||
**API Tokens (PATs)**
|
||
|
||
- Migration 015: `api_tokens` table (PG + SQLite) with SHA-256 hash, prefix, JSON permissions, expiry
|
||
- Token store interface + PG/SQLite implementations (Create, GetByHash, ListForUser, Revoke, CleanExpired, UpdateLastUsed)
|
||
- `POST /api/v1/auth/tokens` — create token (returns plaintext once), permissions validated as subset of user's
|
||
- `GET /api/v1/auth/tokens` — list my tokens; `DELETE /api/v1/auth/tokens/:id` — revoke
|
||
- `POST /api/v1/admin/tokens` — create token for any user (audit logged as `admin.token.create`)
|
||
- Auth middleware: `Bearer arm_pat_...` tokens validated alongside JWTs, user active check, fire-and-forget `last_used_at` update
|
||
- Permission scoping: PAT permissions used directly at request time (git model — retained until revoked)
|
||
- `auth.HashToken()` shared SHA-256 utility (replaces local `hashToken()` in auth.go)
|
||
- Settings UI: API Tokens tab with create form, permission checkboxes, copy-once display, revoke button
|
||
- Admin UI: "PAT" button on user rows creates tokens for any user
|
||
- `BootstrapPAT`: `ARMATURE_BOOTSTRAP_PAT=true` env var creates admin PAT at startup, writes to `/tmp/armature-admin-pat.txt`
|
||
- E2E smoke test: reads bootstrap PAT before falling back to login flow
|
||
|
||
**Extension-Declared User Permissions**
|
||
|
||
- Dynamic permission registry: `RegisterExtensionPermissions()` / `UnregisterExtensionPermissions()` with RWMutex
|
||
- `AllPermissionsWithExtensions()` returns kernel + extension permissions; `AllPermissionsGrouped()` for admin UI
|
||
- `user_permissions` manifest field: extensions declare user-facing permissions
|
||
- `gate_permission` manifest field: ext_api.go checks user permission before calling `on_request`
|
||
- `req["permissions"]` in Starlark request dict: user's effective permissions included for inline checks
|
||
- `permissions.check(user_id, perm)` Starlark module: read-only permission check, always available (no sandbox gate)
|
||
- Group UI: permissions grouped by source (Platform / package name) with section headings
|
||
- Boot-time scan: `RegisterAllExtensionUserPermissions()` populates registry from active packages
|
||
- Uninstall cleanup: `UnregisterExtensionPermissions()` called on package delete
|
||
|
||
**Tests:** 10 new tests (7 handler + 3 auth registry)
|
||
|
||
---
|
||
|
||
## v0.7.6 — Code Hygiene + Test Coverage
|
||
|
||
**Critical Fixes**
|
||
- Removed `channels` from `allowedViews` in `db_module.go` — `ext_view_channels` does not exist; `db.view("channels")` would crash
|
||
- Fixed 5 dead API routes in `workflow.html` — rewired to public workflow API (`/api/v1/public/workflows/`)
|
||
- Fixed `RenderWorkflow` handler to pass route `:id` param as entry token (was reading unset `channel_id`)
|
||
|
||
**Dead Code Removal**
|
||
- Deleted `SeedTestChannel()` from `database/testhelper.go` (inserted into nonexistent channels table)
|
||
- Removed `RunContext.ChannelID` from `sandbox/runner.go` (vestigial, unused)
|
||
- Removed `webhook.Payload.ChannelID` field (channels no longer exist — breaking webhook JSON change)
|
||
- Fixed stale comments in `storage.go`, `prometheus.go`, `workflow_module.go` referencing dead `/channels` paths
|
||
|
||
**Migration Hygiene**
|
||
- Added SQLite placeholder `013_cluster_registry.sql` (PG-only migration, aligns numbering)
|
||
- Renumbered SQLite `013_test_runner_type.sql` → `014` to match PG (compat rename in `migrate.go`)
|
||
- Documented missing migration 008 in both 009 files (merged into 007 during pre-1.0 consolidation)
|
||
|
||
**Test Coverage**
|
||
- 82 new workflow routing tests (`routing_test.go`): `ResolveNextStage`, `ResolveStageByName`, `ParseStageConfig`, `evaluateCondition` with all 10 operators
|
||
- 10 new middleware tests (`permissions_test.go`): `RequirePermission`, `RequireAdmin`, permission caching, `RateLimiter` (allow/deny/fail-open)
|
||
|
||
**Bug Fixes**
|
||
- Removed dead Admin "Storage" tab from System category (backend endpoint preserved for future Monitoring use)
|
||
- Fixed backup download: `sw.auth.token()` → `sw.auth._getToken()` — both "Download Backup" and server backup download now work
|
||
|
||
## v0.7.5 — Headless E2E + CI Gate
|
||
|
||
**CI Gating Redesign**
|
||
- `VERSION` and `scripts/*` no longer trigger frontend/backend tests — deploy-only (pipeline v0.18.0)
|
||
- `docs/*` changes now trigger build-and-deploy (docs are served in-app via Docs surface)
|
||
- Path gating comment block updated to reflect corrected model
|
||
|
||
**E2E Smoke Test**
|
||
- `ci/e2e-smoke-test.sh` — authenticates as admin, discovers surfaces, runs Playwright navigation test
|
||
- `ci/e2e-smoke-driver.js` — visits every surface, asserts shell topbar present, no JS console errors, home link works
|
||
- Screenshot-on-failure: full-page PNG + console log saved as CI artifacts
|
||
- Baseline screenshots captured for every surface (informational, not a gate)
|
||
|
||
**CI Pipeline Integration**
|
||
- `test-runners` stage re-enabled with broad trigger condition (BE/FE/packages/infra)
|
||
- New `e2e-smoke` stage: boots server via docker-compose, runs Playwright smoke test, failure blocks merge
|
||
- `docker-compose.ci.yml` gains `e2e-smoke` service (same Playwright v1.52.0 image)
|
||
- `build-and-deploy` now depends on both `test-runners` and `e2e-smoke`
|
||
|
||
**Documentation**
|
||
- `docs/DESIGN-storage-primitives.md` — v0.8.x storage primitives design (files module, workspace module, capability negotiation, vector columns)
|
||
- `docs/DESIGN-extension-composability.md` — v0.8.4 composability design (slots/contributes manifest fields, lib.require relaxation, SDK helpers)
|
||
- `ROADMAP.md` expanded through v1.0: v0.8.x storage primitives, v0.9.x reference extensions, v0.10.x sidecar tier, v1.0 gate criteria, design principles, full design decisions log
|
||
|
||
## v0.7.4 — Documentation + Deferred Surface Work
|
||
|
||
**Docs Category Grouping**
|
||
- Backend `Category` field on `docEntry` struct in `server/handlers/docs.go`
|
||
- Frontend sidebar groups docs by category with `.docs-category-heading` CSS
|
||
- Four categories: Getting Started, Platform, Extension Development, Operations
|
||
- 14 docs in ordered list (was 7 ordered + 3 auto-discovered)
|
||
|
||
**New Documentation (4 guides)**
|
||
- `PERMISSIONS-AND-GROUPS.md` — RBAC model, 7 permission slugs, system/custom groups, settings cascade, extension permissions
|
||
- `WORKFLOWS.md` — Entry modes, stage modes/types/audiences, signoff gates, SLA enforcement, branch rules, Starlark hooks
|
||
- `STARLARK-REFERENCE.md` — Sandbox constraints, 10 modules with function signatures and permission gates, example hook script
|
||
- `FRONTEND-JS-GUIDE.md` — Preact+htm runtime, 16 SDK modules with API reference, shell topbar patterns, CSS contract
|
||
|
||
**Extension Guide Updates**
|
||
- `config_section` manifest field documented: schema, backend discovery (`configSectionsForSurface()`), frontend `__CONFIG_SECTIONS__` contract, example component
|
||
- Starlark Sandbox API section replaced with pointer to new Starlark Reference
|
||
|
||
**Docs Content Refresh**
|
||
- GETTING-STARTED: `sb_data` → `armature_data` volume name
|
||
- ARCHITECTURE: `sb.register()`/`sb.ns()` → `sw` SDK references, shell topbar mention
|
||
- DEPLOYMENT: `sb_storage` → `armature_storage`, added `TLS_MODE` env var
|
||
- TUTORIAL-FIRST-EXTENSION: `--bg-2` → `--bg-secondary` CSS variable
|
||
- EXTENSION-CSS: self-hosted font notes on `--font` and `--mono`
|
||
- `docs.go`: added `AUDIT-` and `USABILITY-` prefix filters for auto-discovery
|
||
|
||
**Team Admin Workflows Split**
|
||
- `workflows.js` (722 lines) split into 3 ES modules:
|
||
- `workflows.js` (~160 lines) — `WorkflowsSection` + `WorkflowsTab` + imports
|
||
- `workflow-editor.js` (~240 lines) — `WorkflowEditor` + `StageForm`
|
||
- `workflow-monitor.js` (~210 lines) — `AssignmentsTab` + `MonitorTab` + `SignoffPanel`
|
||
- External import contract unchanged (default export stays in `workflows.js`)
|
||
|
||
**Bug Fixes**
|
||
- Docs outline `scrollToHeading` now scrolls `.docs-content` container instead of `scrollIntoView`, preventing topbar from being pushed off-screen
|
||
- `--bg-2` (undefined CSS variable) replaced with `--bg-secondary` in `sw-shell.css` and `sw-primitives.css`
|
||
|
||
## v0.7.3 — Extension Shell Migration
|
||
|
||
**Shell Topbar Migration**
|
||
- Migrated Chat, Notes, and Schedules from legacy `sw.shell.Topbar` component to the v0.7.0 shell topbar contract (`sw.shell.topbar.setTitle/setSlot`)
|
||
- Eliminated double topbar (shell-injected + surface-owned) on all three extension surfaces
|
||
- Chat: reactive slot updates for thread title + People button when conversation changes
|
||
- Notes: slot content for + New Note, Import .md, and Graph toggle buttons
|
||
- Schedules: reactive slot with schedule count and + New Schedule button; removed legacy fallback branch
|
||
|
||
**Runner Test Updates**
|
||
- Added `shell-topbar` test suite to chat-runner, notes-runner, and schedules-runner
|
||
- Tests fetch surface JS and assert: no legacy `sw.shell.Topbar` reference, uses `sw.shell.topbar.setTitle/setSlot` API
|
||
- 6 new tests across 3 runners
|
||
|
||
**Package Versions**
|
||
- Chat surface v0.3.0, Notes surface v0.9.0, Schedules surface v0.2.0
|
||
- Chat runner v0.2.0, Notes runner v0.2.0, Schedules runner v0.2.0
|
||
|
||
**Roadmap**
|
||
- Headless E2E automation moved to v0.7.5 (independent from shell migration)
|
||
|
||
## v0.7.2 — Package Runners + CI Gate
|
||
|
||
**Package Runners (5)**
|
||
- Notes runner: `requires: ["notes"]`. 3 suites (crud, folders, tags-search), 12 tests
|
||
- Chat runner: `requires: ["chat", "chat-core"]`. 2 suites (conversations, messaging), 9 tests
|
||
- Schedules runner: `requires: ["schedules"]`. 1 suite (crud), 5 tests
|
||
- Workflow runner: `requires: ["content-approval"]`. 1 suite (lifecycle), 5 tests
|
||
- Renderer runner: `requires: ["mermaid-renderer"]`. 1 suite (contract), 4 tests
|
||
|
||
**Runner Result API**
|
||
- `POST /api/v1/admin/test-runners/results` — store structured run results
|
||
- `GET /api/v1/admin/test-runners/results` — retrieve latest results per runner
|
||
- In-memory store with 4 Go handler tests
|
||
|
||
**CI Integration**
|
||
- `test-runners` stage in Gitea CI pipeline
|
||
- Playwright driver launches headless browser, navigates to `/s/test-runners`, triggers run-all
|
||
- `wait-for-healthy.sh` polls `/healthz/ready` before test execution
|
||
- DinD networking fix: resolve container IP via `docker inspect` (port mapping not exposed to runner localhost)
|
||
|
||
## v0.7.1 — Surface Runner Framework
|
||
|
||
**`sw.testing` SDK Module**
|
||
- New kernel SDK module at `src/js/sw/sdk/testing.js`
|
||
- `sw.testing.suite(name, fn)` — register test suites with lifecycle hooks
|
||
- `sw.testing.run(name?)` — execute one or all suites, returns structured JSON
|
||
- Suite context: `s.test()`, `s.beforeAll/afterAll()`, `s.beforeEach/afterEach()`, `s.track()`, `s.skip()`
|
||
- Test context: `t.assert.ok/eq/neq/gt/match/throws/status/shape/arrayOf`, `t.warn()`, `t.skip()`
|
||
- Auto-cleanup: `track(type, id)` registers resources for LIFO deletion in afterAll
|
||
- Three result statuses: passed / failed / warned — warnings are never silent
|
||
|
||
**`test-runner` Manifest Type**
|
||
- New `"type": "test-runner"` in `ValidateManifest` — surface-like packages discovered by type
|
||
- Test-runner packages excluded from sidebar nav (not type "surface" or "full")
|
||
- Runner manifests support `"requires": [...]` — missing packages → clean skip
|
||
|
||
**ICD Runner Migration**
|
||
- Migrated from hand-rolled `T.test()`/`T.assert()` framework to `sw.testing.suite()`
|
||
- Stripped extension-dependent tests (channels, notes, personas, etc.) — those belong in v0.7.2 package runners
|
||
- Kernel-only suites: smoke, crud (admin, profile, notifications, teams, workflows, extensions, surfaces, packages), authz, security, providers, packaging, sdk
|
||
- Type changed to `"test-runner"`, standalone route removed, UI rendering delegated to registry
|
||
|
||
**SDK Runner Migration**
|
||
- Migrated from `T.dualTest()`/`T.domains` framework to `sw.testing.suite()`
|
||
- Dual-path validation preserved: SDK call + raw ICD fetch + verdict dispatch
|
||
- Stripped extension domains — kernel-only suites: misc, workflows, admin, packages, connections, dependencies, composition
|
||
- SHAPE_BUG verdict maps to `t.warn()`, SDK_BUG/ICD_BUG to `t.assert.ok(false)`
|
||
|
||
**Runner Registry Surface**
|
||
- New `test-runners` surface at `/s/test-runners` (admin-only)
|
||
- Discovers installed test-runner packages via admin packages API
|
||
- Dynamically loads each runner's JS to register suites
|
||
- Run All button, per-runner Run button, real-time results dashboard
|
||
- Suite/test results with pass/fail/warn/skip color coding, timing, error details
|
||
- Export Failures / Export Full Results buttons for JSON download
|
||
- `requires` checking with prominent skip display for missing dependencies
|
||
- Dark mode styling using kernel CSS variables
|
||
|
||
**Database Migration**
|
||
- SQLite migration 013: adds `test-runner` to packages.type CHECK constraint
|
||
- Postgres migration 014: same constraint update
|
||
|
||
## v0.7.0 — Shell Contract + Surface Audit + Rebrand
|
||
|
||
**Shell Infrastructure**
|
||
- Kernel-injected two-slot topbar for all surfaces (home, left slot, center slot, bell, user menu)
|
||
- `sw.shell.topbar` SDK API: `setLeft()`, `setSlot()`, `setTitle()`, `hide()`, `show()`
|
||
- `.sw-topbar__tabs` / `.sw-topbar__tab` CSS classes for consistent tab styling
|
||
- Shell topbar auto-mounts on extension surfaces via `#shell-topbar` div
|
||
|
||
**Backend WS Events**
|
||
- `package.changed` event broadcast on install/uninstall/enable/disable/update
|
||
- `auth.changed` event targeted to affected user on team/group membership changes
|
||
- `notification.all_read` event split from `notification.read` for cleaner badge sync
|
||
- `Hub.Broadcast()` method for untargeted all-client events
|
||
|
||
**User Menu + Bell Reactivity**
|
||
- User menu re-fetches surface list on `package.changed` and `auth.changed` events
|
||
- Notification bell syncs on `notification.read` and `notification.all_read` across tabs
|
||
|
||
**Surface Migrations**
|
||
- Settings: Pattern B (flat tabs in topbar, no sidebar, full-width content)
|
||
- Admin: Pattern C (category tabs in topbar, surface-owned sidebar below)
|
||
- Team Admin: Pattern B (flat tabs, no sidebar, Groups tab removed)
|
||
- Docs: Pattern A (shell topbar auto-renders, removed explicit Topbar import)
|
||
- All 4 surfaces now have notification bell and user menu via shell topbar
|
||
|
||
**Error Handling + Empty States**
|
||
- `.sw-inline-error` CSS primitive for inline error + retry pattern
|
||
- `.sw-empty-state` CSS primitive for guided empty states
|
||
- Admin Workflows, Packages, Groups: inline error on list fetch failure
|
||
- Admin Workflows, Groups: descriptive empty state guidance
|
||
|
||
**Announcement Global Dismiss**
|
||
- Announcement dismiss state persisted to localStorage keyed by content hash
|
||
- Dismissed once on any surface, dismissed everywhere
|
||
|
||
**Rebrand Assets**
|
||
- `favicon-light.svg` renamed to `wordmark.svg` (was a 520x80 wordmark, not an icon)
|
||
- New `favicon-light.svg`: actual square light-mode icon
|
||
- New `wordmark-dark.svg`, `wordmark-light.svg` for dark/light backgrounds
|
||
- New `favicon-light-32.png`, `favicon-light-256.png` raster icons
|
||
- Full icon library deployed to `src/icons/` (both b/e variants, animated SVGs)
|
||
- `manifest.json` description updated to "Self-hosted extension platform"
|
||
- Light-mode icon entries added to PWA manifest
|
||
|
||
**Bug Fixes**
|
||
- Docs: "On this page" outline links now scroll to headings (IDs were missing from rendered HTML)
|
||
- ICD security tier: tightened path traversal assertion (400/422, not 409), added `finally` cleanup
|
||
- Workflow demo: replaced silent catch with inline error + retry
|
||
- Team Admin: signoff panel shows display names instead of raw UUIDs
|
||
- Deleted `packages/hello-dashboard/` (dead package)
|
||
- Deleted `team-admin/groups.js` (37-line dead-end, no CRUD)
|
||
|
||
## v0.6.18 — CI Bundle Wiring
|
||
|
||
Wire `BUNDLED_PACKAGES` env var into the Gitea CI pipeline so each
|
||
environment gets the correct package set at boot.
|
||
|
||
### Changed
|
||
|
||
- **CI: dev deploys** — `BUNDLED_PACKAGES=*` (install all, matches docker-compose default).
|
||
- **CI: test deploys** — `BUNDLED_PACKAGES=notes,chat,chat-core` (core surfaces only).
|
||
- **CI: prod deploys** — `BUNDLED_PACKAGES=notes,chat,chat-core,mermaid-renderer,schedules`.
|
||
- **docker-compose.yml** — default `BUNDLED_PACKAGES` changed from empty to `*` (install all for local dev).
|
||
|
||
### Fixed
|
||
|
||
- **TestBundledInstall_DefaultAllowlist** — updated test assertions to match v0.6.17's empty default set (was still expecting `notes` in curated defaults).
|
||
|
||
## v0.6.17 — Bug Fixes & Welcome Logic
|
||
|
||
Fixes broken UI interactions (folder creation, team member add), dropdown
|
||
overflow, welcome surface auto-disable, and bare-install default behavior.
|
||
|
||
### Fixed
|
||
|
||
- **Notes "Add folder" button** — `prompt()` replaced with `sw.prompt()`
|
||
so the dialog renders correctly in the extension iframe sandbox.
|
||
- **Admin "Add team members"** — user list API returns `{data:[…]}`; handler
|
||
now unwraps the envelope (`Array.isArray(u) ? u : u.data`) so the user
|
||
picker populates.
|
||
- **Package filter dropdown overflow** — removed `right:0` constraint on
|
||
`.sw-dropdown__list`, added `min-width:max-content` and `overflow-x:hidden`
|
||
so option labels ("Extension", "Workflow") render fully without a scrollbar.
|
||
- **Admin actions cell wrapping** — switched `.admin-actions-cell` from
|
||
`white-space:nowrap` to flexbox with `flex-wrap:wrap; gap:4px` so buttons
|
||
don't overflow on narrow viewports.
|
||
|
||
### Changed
|
||
|
||
- **Welcome surface auto-disable** — welcome page now redirects to `/` when
|
||
any non-core extension surface is installed. Removed from the topbar
|
||
navigation surface list so it never appears alongside real surfaces.
|
||
- **Zero default bundled packages** — `defaultBundledPackages` map is now
|
||
empty. Fresh installs start bare; use `BUNDLED_PACKAGES` env var to control
|
||
what gets auto-installed per environment (`*` for all, comma-separated list
|
||
for selective).
|
||
- **Bundled filter logic** — `nil` (from `*`) means install all; empty map
|
||
(default) means install nothing. Previous code treated both as "install all".
|
||
- **User menu conditional items** — Docs, Settings, and Team Admin menu
|
||
entries only appear when those surfaces are actually enabled, not assumed.
|
||
- **SDK imperative host mount** — `ToastContainer` and `DialogStack` are now
|
||
auto-mounted by the SDK boot sequence for extension surfaces that lack an
|
||
AppShell, preventing missing toast/dialog hosts.
|
||
|
||
## v0.6.16 — Usability Survey Gate
|
||
|
||
Machine-auditable UI quality gate. Four new audit scripts, a structured survey
|
||
prompt, contrast and touch-target fixes, and Docker Hub documentation correction.
|
||
|
||
### Added
|
||
|
||
- **`scripts/generate-ui-inventory.sh`** — walks all kernel + package CSS,
|
||
extracts every class selector with surface, line number, responsive breakpoints,
|
||
spacing tokens, and font-size usage. Outputs `ui-inventory.json` (1567 entries).
|
||
- **`scripts/check-contrast.sh`** — parses `variables.css` dark/light token
|
||
pairs, computes WCAG AA contrast ratios for 24 semantic text-on-background
|
||
pairings per theme (48 total). Uses AA (4.5:1) for normal text, AA-lg (3.0:1)
|
||
for large/bold text contexts.
|
||
- **`scripts/generate-coverage-matrix.sh`** — 12 kernel primitives × all surfaces
|
||
markdown table. Flags any deprecated component usage (`.btn-primary`, etc.).
|
||
- **`scripts/audit-touch-targets.sh`** — static analysis for 44px minimum mobile
|
||
touch targets on close buttons and interactive elements.
|
||
- **`docs/USABILITY-SURVEY.md`** — structured 8-section prompt (viewport, banners,
|
||
responsive, styling, contrast, touch targets, focus indicators, component
|
||
uniformity) with pass/fail criteria and file paths for automated execution.
|
||
- **Focus indicators** — `:focus-visible` styles on `.sw-btn`, `.sw-input`,
|
||
`.sw-dropdown__trigger`, `.sw-menu__item`, `.sw-tabs__tab`.
|
||
- **Mobile touch targets** — `min-width/min-height: 44px` in `@media (max-width:
|
||
768px)` for `.sw-banner__close`, `.sw-toast__close`, `.sw-dialog__close`,
|
||
`.sw-drawer__close`, `.sw-tabs__arrow`, `.modal-close`, `.sw-tabs__tab`,
|
||
`.sw-dropdown__option`.
|
||
|
||
### Fixed
|
||
|
||
- **WCAG contrast violations** — dark-mode accent darkened from `#6c9fff` to
|
||
`#6493ed` (3.03:1 with white text), dark-mode success from `#22c55e` to
|
||
`#1dab51` (3.00:1). Light-mode `--text-3` darkened from `#8b8da3` to `#787a92`.
|
||
Light-mode `--success-light` and `--warning-light` darkened for badge contrast.
|
||
All 48 pairings now pass.
|
||
- **Docker Hub references** — `docs/DEPLOYMENT.md` and `docs/DISTRIBUTION.md`
|
||
corrected from `ghcr.io/armature/armature` to `gobha/armature` (Docker Hub).
|
||
Builder image corrected to `gobha/armature-builder`. GitHub URL corrected to
|
||
`github.com/gobha/armature`.
|
||
|
||
## v0.6.15 — User Display Audit
|
||
|
||
Every user-facing identity surface now shows human-readable names instead of
|
||
UUIDs, with the canonical fallback chain: `display_name → username → "Unknown"`.
|
||
|
||
### Added
|
||
|
||
- **`GET /api/v1/users/resolve?ids=...`** — batch endpoint returns identity
|
||
records (username, display_name, handle, avatar_url) for up to 100 user IDs.
|
||
Response keyed by ID for O(1) client lookups.
|
||
- **`sw.users` SDK module** — `resolve(id)`, `resolveMany(ids)`,
|
||
`displayName(user)` with 60-second local cache. Surfaces use this instead
|
||
of ad-hoc lookups or stale snapshots.
|
||
- **5 handler tests** for the resolve endpoint (single, multiple, missing,
|
||
empty, no-param).
|
||
|
||
### Changed
|
||
|
||
- **Admin users list** — shows `display_name || username` as primary
|
||
identifier, with username shown as secondary when display_name is set.
|
||
- **Admin teams/groups member lists** — replaced `username || user_id`
|
||
with `display_name || username || 'Unknown'`.
|
||
- **Team-admin members** — dropdown and list now show display_name.
|
||
- **Chat participants** — resolved from users table via `sw.users.resolveMany()`
|
||
instead of relying on creation-time snapshot. Message sender names, typing
|
||
indicators, and participant sidebar all use resolved names.
|
||
- **Dashboard greeting** — added `|| 'Unknown'` terminal fallback.
|
||
- **Team activity log** — capitalized fallback to `'Unknown'`.
|
||
|
||
### Deprecated
|
||
|
||
- **`participants.display_name` column** in chat-core — column retained for
|
||
backward compatibility but UI no longer relies on snapshot values. Comments
|
||
added to `packages/chat-core/script.star` noting deprecation.
|
||
|
||
## v0.6.14 — Visual Polish
|
||
|
||
Systematic cleanup of stale values, self-hosted fonts, and rendering fixes.
|
||
Final visual pass before the v0.6.15 usability survey gate.
|
||
|
||
### Fixed
|
||
|
||
- **v0.6.13 spacing regressions** — added half-step tokens (`--sp-1h` 6px,
|
||
`--sp-2h` 10px) and restored correct padding on `.sw-btn--sm`, `.sw-input`,
|
||
`.sw-menu__item`, `.sw-dropdown__option`, `.sw-tabs__tab`.
|
||
- **Theme settings toggle** — showed resolved theme ("Dark") instead of stored
|
||
mode ("System"). Changed `appearance.js` to read `sw.theme.mode`.
|
||
- **Notes surface scrollbar** — added `overflow: hidden` to `.surface-inner`
|
||
in `base.html`, preventing spurious scrollbar at any scale.
|
||
- **Chat input clipped at high scale** — same `overflow: hidden` fix prevents
|
||
zoomed content from overflowing the surface container.
|
||
- **User menu drift at scale > 100%** — `menu.js` now divides
|
||
`getBoundingClientRect()` coords by the CSS zoom factor, fixing position
|
||
for `position: fixed` menus inside a zoomed ancestor.
|
||
- **Undefined variables** — `--text-secondary` (login), `--text-muted`
|
||
(user picker), `--text-1` (settings toggle) replaced with correct tokens.
|
||
|
||
### Changed
|
||
|
||
- **Stale fallback colors purged** — removed ~65 hex/rgba fallback values
|
||
from `var()` calls across 9 kernel CSS files and 3 extension packages.
|
||
Old gold theme color `#b38a4e` fully eliminated (9 instances).
|
||
- **Self-hosted fonts** — bundled DM Sans and JetBrains Mono woff2 files
|
||
in `src/fonts/`. Replaced Google Fonts `@import` and login.html `<link>`
|
||
with local `@font-face` declarations. Zero external font dependencies.
|
||
- **Consistent border-radius** — added `--radius-sm: 4px` token. Migrated
|
||
~60 hardcoded `border-radius` values across all kernel CSS and 12 extension
|
||
packages to three tokens: `--radius-sm` (4px), `--radius` (8px),
|
||
`--radius-lg` (12px).
|
||
|
||
### Updated
|
||
|
||
- `docs/EXTENSION-CSS.md` — added `--sp-1h`, `--sp-2h` half-step tokens
|
||
and `--radius-sm` to the public CSS contract.
|
||
|
||
## v0.6.13 — Responsive & Spacing
|
||
|
||
Spacing token scale and tablet breakpoint. All kernel CSS and extension
|
||
packages migrated from hardcoded values to design tokens.
|
||
|
||
### Added
|
||
|
||
- **Spacing tokens** (`--sp-1` through `--sp-12`) — 4px-grid scale in
|
||
`variables.css`. Nine stops: 4, 8, 12, 16, 20, 24, 32, 40, 48px.
|
||
Numeric naming (`--sp-N`), rem-based for zoom/font-size respect.
|
||
- **Tablet breakpoint** (`max-width: 1024px`) — new responsive tier
|
||
between mobile (768px) and desktop. Secondary workspace pane narrows
|
||
to 360px, admin sidebar to 120px, settings/admin/editor navs shrink.
|
||
- **Breakpoint documentation** in `EXTENSION-CSS.md` — Mobile (768px),
|
||
Tablet (1024px), Desktop (default).
|
||
- **Spacing guidelines** in `EXTENSION-CSS.md` — token table with
|
||
computed pixel values and usage examples.
|
||
|
||
### Changed
|
||
|
||
- **8 kernel CSS files** migrated to spacing tokens — `sw-primitives.css`,
|
||
`modals.css`, `surfaces.css`, `layout.css`, `sw-shell.css`,
|
||
`primitives.css`, `user-menu.css`, `sw-login.css`. Hardcoded padding,
|
||
margin, and gap values replaced with `var(--sp-N)`.
|
||
- **12 extension packages** migrated — chat, dashboard, editor,
|
||
git-board, hello-dashboard, icd-test-runner, notes, schedules,
|
||
sdk-test-runner, tasks, team-activity-log, workflow-demo.
|
||
- **Login hero breakpoint** normalized from 900px to 1024px (tablet).
|
||
- **Notes mobile breakpoint** normalized from 700px to 768px (standard).
|
||
|
||
## v0.6.12 — Extension CSS Isolation
|
||
|
||
Prefix enforcement prevents extension CSS from leaking into the kernel or
|
||
sibling extensions. All 12 in-tree packages migrated to `.ext-{slug}-*`
|
||
naming convention.
|
||
|
||
### Added
|
||
|
||
- **`data-ext` attribute** on extension mount container — enables scoped
|
||
selectors like `[data-ext="chat"] .ext-chat-app`.
|
||
- **CSS linter** (`scripts/lint-package-css.sh`) — validates that the first
|
||
class selector in every extension CSS rule starts with `.ext-{slug}`.
|
||
Exempts `:root`, `@keyframes`, `@font-face`, `@media`, kernel `.sw-*`
|
||
classes, and CodeMirror `.cm-*` classes.
|
||
- **Kernel CSS contract** (`docs/EXTENSION-CSS.md`) — documents stable
|
||
public classes and CSS variables that extensions may reference. Everything
|
||
else is internal kernel CSS.
|
||
|
||
### Changed
|
||
|
||
- **12 packages migrated** — all class selectors renamed to `.ext-{slug}-*`:
|
||
chat, dashboard, editor, git-board, hello-dashboard, icd-test-runner,
|
||
notes, schedules, sdk-test-runner, tasks, team-activity-log, workflow-demo.
|
||
CSS and JS files updated in lockstep.
|
||
- **`icd-test-runner`** — ID selectors (`#extension-mount`) converted to
|
||
class-based selectors with proper prefix.
|
||
- **`editor` cross-references** — compound selectors referencing notes
|
||
classes updated to new `.ext-notes-*` names.
|
||
- **`chat` kernel overrides** — `.sw-dialog:has(...)` override scoped under
|
||
`[data-ext="chat"]` instead of global.
|
||
|
||
## v0.6.11 — CSS Deduplication
|
||
|
||
One class per concept. The old `primitives.css` button, toast, popup-menu,
|
||
dropdown, and tabs systems are retired. `sw-primitives.css` is the single
|
||
source of truth for all Preact component styles.
|
||
|
||
### Changed
|
||
|
||
- **Buttons**: All 29 files migrated from `.btn-primary` / `.btn-small` /
|
||
`.btn-danger` / `.btn-ghost` / `.btn-md` / `.btn-sm` to the BEM-style
|
||
`.sw-btn .sw-btn--{variant} .sw-btn--{size}` system.
|
||
- **Toasts**: Old `.toast-container` / `.toast` CSS deleted. SDK's
|
||
`sw.toast()` API already used `.sw-toast-*` classes — no JS changes.
|
||
- **Popup menus**: Old `.popup-menu` / `.popup-menu-item` CSS deleted
|
||
(unused — `.sw-menu` is the active system).
|
||
- **Dropdown collision resolved**: Old `.sw-dropdown` (styled `<select>`)
|
||
deleted from `primitives.css`. The `sw-primitives.css` custom dropdown
|
||
component (`.sw-dropdown` with BEM sub-elements) is authoritative.
|
||
- **Tabs collision resolved**: Old `.sw-tabs` / `.sw-tab-btn` deleted from
|
||
`primitives.css`. The `sw-primitives.css` scrollable tabs component
|
||
(`.sw-tabs__tab`) is authoritative.
|
||
- **`.settings-section` collision resolved**: Removed duplicate definition
|
||
from `modals.css`. The `surfaces.css` card-style definition is
|
||
authoritative; `.settings-content .settings-section` override resets
|
||
card styling for flat settings layouts.
|
||
|
||
### Added
|
||
|
||
- `.sw-btn--success` variant in `sw-primitives.css` (green action button).
|
||
- `--bg-active` CSS variable in both dark/light themes (`variables.css`).
|
||
- `scripts/audit-css-collisions.sh` — finds duplicate class selectors
|
||
across kernel CSS files and outputs a JSON collision report.
|
||
|
||
### Fixed
|
||
|
||
- `packages/sdk-test-runner/css/main.css`: Wrong variable names
|
||
(`--text3` → `--text-3`, `--text2` → `--text-2`, `--bg1`/`--bg2` →
|
||
`--bg-raised`).
|
||
- `packages/icd-test-runner/css/main.css`: Replaced inline button fallback
|
||
styles with kernel `.sw-btn` system.
|
||
|
||
### Removed
|
||
|
||
- Old button classes: `.btn-primary`, `.btn-small`, `.btn-danger`,
|
||
`.btn-full`, `.btn-ghost`, `.btn-subtle`, `.btn-sm`, `.btn-md`.
|
||
- Old toast classes: `.toast-container`, `.toast`, `.toast.error/warning/success`.
|
||
- Old popup menu classes: `.popup-menu`, `.popup-menu-item`, `.popup-menu-*`.
|
||
- Old dropdown and tabs definitions from `primitives.css` that collided
|
||
with `sw-primitives.css`.
|
||
|
||
## v0.6.10 — Viewport Foundation
|
||
|
||
Single layout model. Every surface renders inside one containment chain:
|
||
`body → shell → surface`. No dual systems. No transform hacks.
|
||
|
||
### Changed
|
||
|
||
- **CSS `zoom` replaces `transform: scale()`**: UI scale (80%–175%) now
|
||
uses CSS `zoom` on `#surfaceInner` instead of `transform: scale()`.
|
||
`zoom` reflows layout correctly — `getBoundingClientRect()` returns
|
||
accurate values, eliminating the scale-correction hack in `menu.js`.
|
||
Supported in all evergreen browsers (Firefox 126+, June 2024).
|
||
- **Single layout root**: `<body>` in `base.html` is the authoritative
|
||
flex column layout. `.sw-shell` CSS demoted from viewport-level
|
||
container (`height: 100vh`) to fill-parent (`height: 100%`).
|
||
Safe-area insets moved from `.sw-shell` to `<body>`.
|
||
- **Banner single source of truth**: Template banners measure their own
|
||
height via inline `<script>` and set `--banner-top-height` /
|
||
`--banner-bottom-height` CSS variables. Removed `--banner-h: 28px`
|
||
fixed variable. `ShellBanner` Preact component's `useEffect`
|
||
measurement removed (dead code — no surface imports `AppShell`).
|
||
- **`sw-shell__banner` position**: Changed from `position: fixed` to
|
||
`position: static` — template banners are in-flow elements.
|
||
- **`sw-shell__body` padding**: Removed `padding-top/bottom` for
|
||
banner offsets — unnecessary with in-flow banners.
|
||
- **Extension surfaces `100vh` → `100%`**: `chat-app`, `chat-loading`,
|
||
`surface-dashboard` now use `height: 100%` to inherit from the
|
||
extension mount container (like Notes). Fixes overflow behind banners.
|
||
- **`100vh` → `100dvh` fallbacks**: All viewport-height declarations
|
||
use `height: 100vh; height: 100dvh;` pattern for correct behavior on
|
||
mobile browsers. Affects: `base.html`, `sw-login.css`,
|
||
`workflow.html`, `workflow-landing.html`, `primitives.css`,
|
||
`git-board/css/main.css`.
|
||
- **`sw.shell.getScale()` deprecated**: Returns `1` always — CSS `zoom`
|
||
handles layout reflow without manual correction.
|
||
|
||
### Deprecated
|
||
|
||
- `src/js/sw/shell/app-shell.js`, `app.js`, `surface-viewport.js` —
|
||
no surface imports these. Layout root is `<body>` in `base.html`.
|
||
|
||
## v0.6.9 — Session Lifetime Config
|
||
|
||
Admin-configurable session durations, "keep me logged in" opt-in, and
|
||
optional idle timeout. Completes the auth hardening started in v0.6.8.
|
||
|
||
### Added
|
||
|
||
- **Admin session settings**: `session.access_token_ttl` (default 15m,
|
||
clamp 5m–60m) and `session.refresh_token_ttl` (default 7d, clamp
|
||
1h–90d) stored in `global_settings`. New `LoadSessionConfig()` helper
|
||
reads and clamps values with `parseDurationString()` supporting `m`,
|
||
`h`, `d` suffixes.
|
||
- **"Keep me logged in" checkbox**: Login form opt-in. Checked = full
|
||
`refresh_token_ttl`. Unchecked = capped at 24h. Cookie `max-age`
|
||
tracks whichever lifetime was chosen. `keep_login` flag stored on
|
||
refresh token row.
|
||
- **Config-driven token generation**: `generateTokens()` reads TTLs from
|
||
`global_settings` instead of hardcoded `15*time.Minute` /
|
||
`7*24*time.Hour`. Response includes `expires_in` and
|
||
`refresh_expires_in` (seconds) so the client schedules refresh
|
||
correctly.
|
||
- **Idle timeout (optional)**: Admin-toggleable (default off). Server
|
||
checks `last_activity_at` on refresh-token row; rejects if gap exceeds
|
||
`session.idle_timeout`. Client SDK pings `POST /api/v1/auth/activity`
|
||
on click/keydown (debounced, max 1/min).
|
||
- **Admin Settings > Session section**: Dropdowns for access TTL, refresh
|
||
TTL, and idle timeout with toggle.
|
||
- **7 new tests**: Duration parsing, clamping (low/high), defaults,
|
||
invalid values, empty idle timeout.
|
||
|
||
### Changed
|
||
|
||
- `CreateRefreshToken` store method now accepts `keepLogin bool`
|
||
parameter; both Postgres and SQLite implementations updated.
|
||
- `GetRefreshTokenInfo` returns `RefreshTokenInfo` struct with
|
||
`UserID`, `KeepLogin`, `LastActivityAt` for idle-timeout decisions.
|
||
- SDK `auth.login()` accepts optional third `keepLogin` parameter;
|
||
cookie max-age derived from server `refresh_expires_in` response.
|
||
- SDK boots activity tracking after successful auth boot.
|
||
|
||
## v0.6.8 — Cookie Fix + UI Hardening Roadmap
|
||
|
||
### Fixed
|
||
|
||
- **Session cookie max-age bug**: `arm_token` cookie was set to 15 min
|
||
(matching access token) while refresh token lasted 7 days. Cookie now
|
||
matches refresh token lifetime so Go SSR middleware can serve page
|
||
shells while JS refreshes the access token client-side.
|
||
|
||
### Added
|
||
|
||
- **ROADMAP-UI.md**: Detailed UI hardening roadmap (v0.6.9–v0.6.15)
|
||
covering session config, viewport foundation, CSS deduplication,
|
||
extension CSS isolation, responsive layout, visual polish, and
|
||
automated usability survey gate.
|
||
|
||
## v0.6.7 — Native mTLS
|
||
|
||
End-to-end mutual TLS without a reverse proxy. Targets systemd+podman
|
||
deployments where the Go binary terminates TLS itself.
|
||
|
||
### Added
|
||
|
||
- **`TLS_MODE` config**: Three values — `none` (default, plain HTTP), `server`
|
||
(TLS, no client cert), `mtls` (mutual TLS, client cert required). Independent
|
||
of `AUTH_MODE`. Combinations: `server`+`builtin` for HTTPS with password auth,
|
||
`mtls`+`mtls` for full mTLS identity, `none`+`mtls` for proxy-terminated
|
||
(existing behavior).
|
||
- **TLS server mode**: Binary calls `ListenAndServeTLS` directly when
|
||
`TLS_MODE` is `server` or `mtls`. TLS 1.3 minimum, no cipher suite
|
||
configuration. Config: `TLS_CERT`, `TLS_KEY`, `TLS_CA` path env vars.
|
||
- **`MTLSNativeProvider`**: New auth provider that reads
|
||
`r.TLS.PeerCertificates[0]` directly — no header trust. `Subject.CommonName`
|
||
becomes username, `sha256(cert.Raw)` becomes `external_id`. Auto-provisions
|
||
users identically to the proxy provider. Selected when `AUTH_MODE=mtls` and
|
||
`TLS_MODE=mtls`.
|
||
- **Shared mTLS helpers**: `ParseDN()`, `FingerprintCert()`, and
|
||
`resolveOrProvision()` extracted to `mtls_helpers.go`. Both proxy and native
|
||
providers consume the same user resolution logic.
|
||
- **Peer TLS config**: `BuildPeerTLSConfig()` constructs a `*tls.Config` for
|
||
outbound node-to-node connections (forward-looking — cluster registry is
|
||
currently DB-backed with no HTTP peer calls).
|
||
- **`armature-ca.sh`**: Shell wrapper around openssl for cert provisioning.
|
||
Three commands: `init` (CA keypair), `issue-node` (365d, ServerAuth +
|
||
ClientAuth EKU), `issue-user` (90d, ClientAuth only). All ECDSA P-256, PEM
|
||
output.
|
||
- **12 new tests**: `FingerprintCert` determinism, native provider unit tests
|
||
(nil TLS, empty certs, no CN, email extraction), TLS integration tests (no
|
||
cert rejected, wrong CA rejected, expired cert rejected, valid cert accepted,
|
||
peer certificate visibility).
|
||
|
||
### Changed
|
||
|
||
- **`MTLSProvider` → `MTLSProxyProvider`**: Renamed for clarity. Config type
|
||
`MTLSConfig` → `MTLSProxyConfig`. Constructor `NewMTLSProvider` →
|
||
`NewMTLSProxyProvider`. File `mtls.go` → `mtls_proxy.go`.
|
||
|
||
## v0.6.6 — Final Hardening
|
||
|
||
Final pass before public release. Security, correctness, and developer
|
||
experience. No new features — only fixes, validation, and cleanup.
|
||
|
||
### Added
|
||
|
||
- **`ValidateManifest()` gate**: Centralized manifest validation function
|
||
(`package_validate.go`) called at both upload-install and bundled-install
|
||
time. Catches malformed manifests early with clear error messages. 12 unit
|
||
tests covering all type constraints and edge cases.
|
||
- **Extension dependency auto-activation**: Installing a package whose
|
||
`dependencies` list an uninstalled library will auto-install that library
|
||
from the bundled packages directory. If the dependency is not bundled, the
|
||
error message lists exactly what's missing.
|
||
- **`OptionalAuth` middleware**: New page middleware for workflow visitor
|
||
routes. Authenticates if a token is present, allows anonymous pass-through
|
||
otherwise. Replaces the stale `AuthOrRedirect` TODO for Session routes.
|
||
- **Package signing schema reservation**: `signature` field accepted in
|
||
manifest (no-op). `PACKAGE_VERIFY_SIGNATURES` env var logs warnings for
|
||
unsigned packages when enabled. No cryptographic verification yet — schema
|
||
slot reserved to prevent a breaking change later.
|
||
- **ICD/SDK runner v0.6.x coverage**: Smoke tier adds metrics, cluster,
|
||
backups, docs, and OpenAPI JSON endpoints. SDK admin domain adds metrics,
|
||
cluster, and backups dual-path tests.
|
||
|
||
### Fixed
|
||
|
||
- **OIDC nonce validation**: ID token nonce claim is now validated against
|
||
the stored authorization request nonce. Previously the nonce was generated
|
||
and stored but never checked on callback — a token replay/substitution
|
||
vulnerability.
|
||
|
||
### Changed
|
||
|
||
- **Schema migration stub**: `RunSchemaMigrations()` no longer pretends to
|
||
work. Replaced with a log-only function documenting that only additive
|
||
schema changes are supported. Downgrade rejection preserved.
|
||
- **Session middleware TODO resolved**: `main.go` Session slot now uses
|
||
`OptionalAuth` instead of `AuthOrRedirect`. OIDC nonce TODO and migration
|
||
stub TODO also resolved.
|
||
|
||
## v0.6.5 — Renderer Pipeline + Docs Rewrite
|
||
|
||
Lifts block rendering to a kernel SDK primitive so all surfaces share one
|
||
markdown pipeline. Rewrites docs for external audience. Adds CONTRIBUTING
|
||
guide and extension tutorial.
|
||
|
||
### Added
|
||
|
||
- **`sw.renderers` SDK module**: Kernel-level renderer registry. Extensions
|
||
register block renderers (fenced code blocks) and post renderers (DOM
|
||
post-processing) once; all surfaces consume via `sw.markdown`.
|
||
- **`sw.markdown` SDK module**: Unified markdown rendering via `marked` v16
|
||
(vendored). Lazy-loads `marked` + DOMPurify. Custom code hook delegates
|
||
fenced blocks to `sw.renderers`. Wikilink tokenizer (`[[Page Name]]`)
|
||
built in for all surfaces.
|
||
- **Browser extension script loader**: Server injects `<script>` tags for
|
||
enabled browser-tier extensions into all pages. Extensions register via
|
||
`sw:ready` event.
|
||
- **Chat markdown support**: Messages with `content_type: 'markdown'` render
|
||
via `sw.markdown`. Existing `text` messages stay plain (no behavior change).
|
||
- **Mermaid architecture diagrams**: Six diagrams in `docs/ARCHITECTURE.md`:
|
||
system overview, request flow, extension lifecycle, realtime events,
|
||
settings cascade, cluster topology.
|
||
- **`CONTRIBUTING.md`**: Development setup, project structure, test commands,
|
||
code conventions.
|
||
- **`docs/TUTORIAL-FIRST-EXTENSION.md`**: Step-by-step guide to building and
|
||
installing a browser extension.
|
||
- **`docs/DESIGN-WORKFLOWS.md`**: Clean workflow system design document.
|
||
- **13 new renderers tests** (`src/js/__tests__/renderers.test.js`).
|
||
|
||
### Changed
|
||
|
||
- **Notes surface**: Hand-rolled markdown renderer replaced with
|
||
`sw.markdown.renderSync()`. Post-renderers (mermaid, katex, etc.) now
|
||
execute on preview. ~90 lines deleted.
|
||
- **Docs surface**: Hand-rolled markdown renderer replaced with
|
||
`sw.markdown.renderSync()`. ~160 lines deleted.
|
||
- **4 renderer extensions** (mermaid, katex, csv-table, diff-viewer):
|
||
Rewritten from dead `Extensions.register()` pattern to IIFE +
|
||
`sw.renderers.register()` via `sw:ready` event. All rendering logic
|
||
preserved.
|
||
- **Surface alias routes removed**: Six `/api/v1/admin/surfaces/*` backward
|
||
compat routes deleted from `main.go`. SDK and ICD runner migrated to
|
||
canonical `/api/v1/admin/packages/*` endpoints.
|
||
- **Docs rewritten for external audience**: All references to fork history,
|
||
internal codenames, and pre-fork version numbers removed from docs,
|
||
CHANGELOG, and ROADMAP.
|
||
|
||
### Removed
|
||
|
||
- `docs/DESIGN-WORKFLOW-REDESIGN-0.2.6.md` — replaced by `DESIGN-WORKFLOWS.md`.
|
||
- Hand-rolled markdown renderers in Notes and Docs surfaces.
|
||
- Six `/admin/surfaces/*` API alias routes.
|
||
|
||
---
|
||
|
||
## v0.6.4 — Admin Health/Metrics Tab + Cluster Merge
|
||
|
||
Structural consolidation: cluster dashboard merges into Admin as a Health tab.
|
||
Comprehensive metrics endpoint for runtime, DB, cluster, and extension stats.
|
||
|
||
### Added
|
||
|
||
- **Admin Health tab**: New "Health" section under Monitoring in the Admin
|
||
surface. Auto-refreshing panels for runtime, database, cluster, and extension
|
||
metrics with configurable poll interval (5/10/30/60s).
|
||
- **`GET /api/v1/admin/metrics`**: Single JSON endpoint returning all platform
|
||
metrics — runtime (goroutines, heap, GC, uptime, FDs), DB pool (latency,
|
||
active/idle/max, PG-only dead tuples + active backends), cluster nodes
|
||
(when PG multi-node), and extension stats (Starlark exec/errors/duration,
|
||
trigger fires, event bus publish/deliver counts).
|
||
- **Fattened heartbeat payload**: Cluster heartbeat JSONB now carries stack
|
||
usage, GC CPU%, extensions loaded, Starlark execution counters, and trigger
|
||
fire count — visible in per-node cluster cards.
|
||
- **Sandbox execution tracking**: `ExecPackage` and `CallEntryPoint` now
|
||
increment atomic counters (exec, errors, duration) and the previously
|
||
declared but unused `SandboxExecutionsTotal` Prometheus counter.
|
||
- **Event bus counters**: `Bus.PublishCount()` and `Bus.DeliverCount()` track
|
||
cumulative publish/deliver operations.
|
||
- **Trigger fire counter**: `Engine.FireCount()` tracks cumulative trigger
|
||
fires across webhook, event, and scheduled triggers.
|
||
- 4 new handler tests (metrics SQLite shape, cluster shape, extension counters).
|
||
- 1 new cluster test (fattened heartbeat payload).
|
||
|
||
### Changed
|
||
|
||
- **Health endpoint consolidation**: `/health` and `/api/v1/health` now use a
|
||
shared `buildHealthResponse()` function returning identical JSON. Both now
|
||
include `registration_enabled` when the database is connected.
|
||
- **Block renderer `requires` removed**: `mermaid-renderer`, `katex-renderer`,
|
||
`csv-table`, and `diff-viewer` no longer require `["chat"]` — they activate
|
||
on any surface (notes, docs, etc.).
|
||
|
||
### Removed
|
||
|
||
- **`cluster-dashboard` package**: Standalone surface package retired. Cluster
|
||
node visibility is now in Admin > Monitoring > Health.
|
||
|
||
---
|
||
|
||
## v0.6.3 — Dead Code Sweep + Registry Fix
|
||
|
||
Hardening: fix broken registry install, add registry settings UI,
|
||
clean up dead code, narrow default bundle.
|
||
|
||
### Fixed
|
||
|
||
- **Registry install**: SDK was sending `{ url }` but the Go handler expects
|
||
`{ download_url }`, causing every registry Install click to return 400.
|
||
|
||
### Added
|
||
|
||
- **Package Registry settings**: New "Package Registry" section in Admin >
|
||
Settings with a URL input field for configuring the external registry.
|
||
- **`scripts/generate-registry.sh`**: Shell script that reads `.pkg` ZIPs,
|
||
extracts manifests, and emits a `registry.json` for self-hosted discovery.
|
||
- **`docs/PACKAGE-REGISTRY.md`**: Documents registry JSON format, admin
|
||
configuration, script usage, and self-hosting setup.
|
||
|
||
### Removed
|
||
|
||
- **Dead Go code**: Orphaned channel/message type comments from
|
||
`interfaces.go`, unused `roleFilterType()` from `pages.go`, stale version
|
||
comment from `main.go`.
|
||
- **Dead vendor JS**: `marked.min.js` (40KB) and `purify.min.js` (22KB) —
|
||
unused vendor copies with zero production imports. Removed cache entries
|
||
from `sw.js`.
|
||
- **`dev.html`**: 676-line standalone dev gallery, not referenced by anything.
|
||
- **Stale version comments**: Stripped legacy version annotations across
|
||
~140 files — standalone comments, inline suffixes, section headers, and
|
||
config field docs. Reworded to describe features rather than reference
|
||
obsolete internal version numbers.
|
||
|
||
### Changed
|
||
|
||
- **Default bundle narrowed** from 10 packages to 5: `notes`, `chat`,
|
||
`chat-core`, `mermaid-renderer`, `schedules`. All other packages remain
|
||
available via `BUNDLED_PACKAGES=*` or explicit list.
|
||
|
||
---
|
||
|
||
## v0.6.2 — Docs Polish + Dynamic OpenAPI
|
||
|
||
Docs surface polish and dynamic OpenAPI spec with extension route merging.
|
||
|
||
### Added
|
||
|
||
- **Dynamic OpenAPI spec**: `GET /api/docs/openapi.json` builds a merged
|
||
spec at request time — kernel endpoints + auto-generated stubs for every
|
||
extension `api_routes` entry. Extensions may optionally declare `api_schema`
|
||
in `manifest.json` for richer path items (params, body, response examples).
|
||
- **`api_schema` manifest field**: Optional array in `manifest.json`. Malformed
|
||
entries are logged and skipped — never blocks extension loading.
|
||
- **Tests**: 7 new handler tests (zero extensions, stubs, rich schema,
|
||
multi-extension, malformed schema, disabled exclusion, required fields).
|
||
|
||
### Fixed
|
||
|
||
- **Dark mode contrast**: Replaced all hardcoded light-mode fallbacks in docs
|
||
CSS with theme-aware CSS variables. Tables, code blocks, nav items, and
|
||
headings now readable in dark mode.
|
||
- **Docs loading state**: Replaced borrowed `settings-placeholder` animation
|
||
with docs-specific pulse skeleton. Added error state with retry button.
|
||
- **Docs routing**: Default route (`/docs`) now redirects to
|
||
`/docs/GETTING-STARTED` instead of the blank `/:section` placeholder.
|
||
- **Docs scrolling**: Content area now scrolls correctly — full document
|
||
visible without text overflowing viewport.
|
||
- **Docs icon**: Changed from 🧩 to 📖 in the user menu.
|
||
- **Docs duplicate menu entry**: Docs was appearing twice (once from surfaces
|
||
API, once from hardcoded items). Added `'docs'` to `CORE_IDS` filter.
|
||
- **Swagger UI**: Updated to point at dynamic `/api/docs/openapi.json`
|
||
endpoint. Static YAML preserved for backward compatibility.
|
||
- **OpenAPI tests on Postgres**: `openapiTestStores()` was hardcoded to
|
||
`sqlite.NewStores()` — caused `pq: syntax error at or near ","` in CI.
|
||
Now uses `database.IsSQLite()` check like all other handler tests.
|
||
|
||
### API
|
||
|
||
| Endpoint | Method | Description |
|
||
|----------|--------|-------------|
|
||
| `/api/docs/openapi.json` | GET | Dynamic merged OpenAPI 3.0 spec |
|
||
|
||
---
|
||
|
||
## v0.6.1 — Backup/Restore + Documentation
|
||
|
||
Operational tooling for data safety and extension authoring.
|
||
|
||
### Added
|
||
|
||
- **Backup/Restore**: Full-instance backup as `.swb` ZIP archive containing
|
||
core tables (JSONL), ext_data tables, and package assets. Stream to client
|
||
or save server-side. Restore wipes and rebuilds from archive. Dialect-neutral
|
||
(works across SQLite and Postgres).
|
||
- **Admin backup section**: New "Backup" tab under `/admin` with create,
|
||
download, delete, and restore operations. Destructive restore requires
|
||
confirmation dialog.
|
||
- **Documentation surface**: Builtin surface at `/docs` with sidebar navigation
|
||
and client-side markdown rendering. Ships with 5 documents: Getting Started,
|
||
Extension Guide, API Reference, Deployment, Package Format.
|
||
- **Docs API**: `GET /api/v1/docs` lists available documents, `GET /api/v1/docs/:name`
|
||
returns raw markdown content. Authenticated (all users).
|
||
- **Tests**: 6 backup handler tests + E2E script (`ci/e2e-backup-test.sh`).
|
||
|
||
### API
|
||
|
||
| Endpoint | Method | Description |
|
||
|----------|--------|-------------|
|
||
| `/api/v1/admin/backup` | POST | Create backup (stream or `?store=true`) |
|
||
| `/api/v1/admin/backups` | GET | List server-side backups |
|
||
| `/api/v1/admin/backups/:name` | GET | Download backup |
|
||
| `/api/v1/admin/backups/:name` | DELETE | Delete backup |
|
||
| `/api/v1/admin/restore` | POST | Restore from `.swb` upload |
|
||
| `/api/v1/docs` | GET | List documentation |
|
||
| `/api/v1/docs/:name` | GET | Get document content |
|
||
|
||
---
|
||
|
||
## v0.6.0 — Cluster Registry + HA
|
||
|
||
MVP convergence point. PG-backed cluster registry for horizontal scaling —
|
||
zero new infrastructure (no etcd/Consul/Redis).
|
||
|
||
### Added
|
||
|
||
- **Cluster registry**: `node_registry` UNLOGGED table with self-registration
|
||
on startup (`INSERT ... ON CONFLICT DO UPDATE`), heartbeat tick (default 10s),
|
||
stale sweep (default 30s threshold), and self-eviction (`os.Exit(1)` when
|
||
swept by a peer — K8s restarts the process).
|
||
- **Cluster admin API**: `GET /api/v1/admin/cluster` returns all registered
|
||
nodes with runtime stats (goroutines, heap, GC, uptime, ws_clients) in the
|
||
standard `{data: [...]}` envelope.
|
||
- **Health endpoint cluster info**: `GET /health` and `GET /api/v1/health`
|
||
now include `node_id` and `cluster: {size, peers, heartbeat_age_ms}` when
|
||
running on Postgres with the registry active.
|
||
- **Cluster dashboard**: `cluster-dashboard` admin surface package renders one
|
||
card per node with live runtime stats. Auto-refreshes every 10s. Stats are
|
||
JSONB — new keys render automatically without schema migration.
|
||
- **Cluster config**: `CLUSTER_NODE_ID` (default hostname-PID),
|
||
`CLUSTER_HEARTBEAT_INTERVAL` (default 10s), `CLUSTER_STALE_THRESHOLD`
|
||
(default 30s), `CLUSTER_ENDPOINT` (Phase 2 mesh, auto-detect).
|
||
- **3-replica E2E test**: `docker-compose-e2e.yml` now runs 3 replicas.
|
||
`ci/e2e-cluster-test.sh` verifies registration, stale sweep on stop, and
|
||
re-registration on restart.
|
||
- **5 new tests**: 3 cluster registry unit tests (stats collection, start/stop
|
||
lifecycle, node ID) + 2 handler tests (list nodes, empty response).
|
||
- **Curated bundle expanded**: `chat` and `cluster-dashboard` added to the
|
||
default bundle (now 10 packages). Production deployments can override with
|
||
`BUNDLED_PACKAGES=notes,chat,chat-core,cluster-dashboard` for a lean set.
|
||
|
||
### Changed
|
||
|
||
- SQLite deployments are unaffected — cluster store is nil, all cluster code
|
||
is guarded behind `database.IsPostgres() && stores.Cluster != nil`.
|
||
Cluster dashboard gracefully shows "0 nodes registered" on SQLite.
|
||
|
||
## v0.5.5 — Upgrade Testing
|
||
|
||
### Fixed
|
||
|
||
- **Bundled permission auto-grant on Postgres**: The `InstallBundledPackages`
|
||
auto-grant SQL used SQLite-style `granted = 1` / `granted = 0` which fails
|
||
on Postgres BOOLEAN columns. Now dialect-aware: `true`/`false` on Postgres,
|
||
`1`/`0` on SQLite. This caused bundled extensions (notes, chat-core, etc.)
|
||
to install with permissions not granted on Postgres deployments.
|
||
- **E2E test API field corrections**: Fixed `ci/e2e-chat-test.sh` login field
|
||
(`"username"` → `"login"`), token extraction (`"token"` → `"access_token"`),
|
||
and profile endpoint (`/api/v1/me` → `/api/v1/profile`).
|
||
|
||
### Added
|
||
|
||
- **Upgrade test harness**: `docker-compose-upgrade.yml` + `ci/e2e-upgrade-test.sh`
|
||
orchestrate a full upgrade cycle: build old image, seed data (notes,
|
||
conversations, messages, settings), stop old, start new, verify data integrity
|
||
post-upgrade. Covers auth, notes, conversations, packages, and settings.
|
||
- **Rolling upgrade test**: `ci/e2e-upgrade-rolling.sh` tests rolling upgrade
|
||
with 2 replicas sharing Postgres. Upgrades one replica at a time, verifies
|
||
cross-replica reads during the mixed-version window.
|
||
- **11 new Go tests** in `upgrade_test.go`:
|
||
- Schema edge cases: add index (idempotent), add column (idempotent),
|
||
multi-column add, row preservation across migration.
|
||
- Settings migration: global/team/user overrides preserved across package
|
||
update, new keys get defaults, removed keys not deleted.
|
||
- Package compatibility: bundled skip-if-present on restart, dormant status
|
||
for unmet requires, enabled/type preserved across version bump.
|
||
- Direct `MigrateExtTables` tests: new table creation, column add with row
|
||
preservation, index idempotency.
|
||
|
||
## v0.5.4 — Package Updates
|
||
|
||
### Added
|
||
|
||
- **Package update API**: `POST /api/v1/admin/packages/:id/update` accepts a
|
||
`.pkg` archive, validates semver version bump (rejects same/older), applies
|
||
additive schema migration, merges settings, replaces assets, and re-syncs
|
||
permissions/triggers/dependencies.
|
||
- **Package export API**: `GET /api/v1/admin/packages/:id/export` streams the
|
||
installed package as a downloadable `.pkg` ZIP archive. Completes the manual
|
||
rollback story: export before update, re-install old `.pkg` if needed.
|
||
- **Semver parsing**: `ParseSemver` / `Compare` in `handlers/semver.go` with
|
||
prerelease support. Used by the update handler to enforce version bumps.
|
||
- **Additive schema migration**: `MigrateExtTables` diffs declared `db_tables`
|
||
against existing ext_data schema. Adds new columns (`ALTER TABLE ADD COLUMN`)
|
||
and new tables. No destructive changes — columns in DB but absent from
|
||
manifest are preserved. `ListExtColumns` introspects via `PRAGMA table_info`
|
||
(SQLite) or `information_schema` (Postgres).
|
||
- **Settings merge on update**: New manifest setting keys get their declared
|
||
default value; existing admin-configured values are preserved.
|
||
- **Admin UI Update button**: Non-core package cards now show an Update button
|
||
with a confirm dialog before upload.
|
||
- **14 new Go tests**: Version bump, 5 rejection cases (same/older/core/type/id
|
||
mismatch), schema add column with data preservation, schema add table,
|
||
settings merge, export with valid zip, default allowlist, wildcard allowlist.
|
||
|
||
### Changed
|
||
|
||
- **Curated default bundle**: Default bundled packages reduced from 23 to 8
|
||
core packages (notes, chat-core, workflow-chat, dashboard, 4 demo workflows).
|
||
`BUNDLED_PACKAGES=*` installs all; explicit comma-separated list for custom.
|
||
Existing deployments are unaffected (install-once, skip-if-present).
|
||
- **`BUNDLED_PACKAGES` env var**: Empty value now installs curated defaults
|
||
instead of all packages. Use `"*"` for previous install-all behavior.
|
||
|
||
## v0.5.3 — Chat Polish + Integration Testing
|
||
|
||
### Added
|
||
|
||
- **`db.query()` search_like**: New kernel-level `search_like` kwarg for text
|
||
search. Accepts `{col: pattern}` dict, generates OR-joined `LIKE` (SQLite) /
|
||
`ILIKE` (Postgres) clauses. Composes with existing `filters` via AND. Reusable
|
||
by any package. 5 new sandbox tests.
|
||
- **Conversation search**: `GET /search?q=term` endpoint in `chat-core`. Searches
|
||
conversation titles and message content, scoped to user's conversations.
|
||
Returns `{conversations: [...], messages: [...]}`.
|
||
- **Search UI**: Debounced search input in chat sidebar. Results view replaces
|
||
conversation list showing matched conversations and message snippets with
|
||
section headers. Click result to navigate, clear button to dismiss.
|
||
- **`workflow-chat` library package**: `on_advance` hook that creates scoped
|
||
group conversations when workflow stages require team collaboration. Adds all
|
||
team members as participants, sends system message linking to the instance,
|
||
enriches `stage_data` with `conversation_id`. Idempotent — skips if
|
||
conversation already exists.
|
||
- **Multi-user E2E test infrastructure**: `docker-compose-e2e.yml` with 2
|
||
replicas, Postgres, and nginx load balancer. `ci/e2e-chat-test.sh` shell
|
||
script covering auth, conversation CRUD, message send/read, cross-replica
|
||
consistency, search, and pagination. `ci/e2e-ws-listener.js` Node.js
|
||
WebSocket client for realtime event testing.
|
||
- **Workflow hook tests**: 6 new Go tests for `parseOnAdvanceResult` covering
|
||
None, nil, error, enriched stage_data, non-dict, and nested structure cases.
|
||
|
||
### Changed
|
||
|
||
- **Message pagination polish**: Scroll position now preserved when loading older
|
||
messages — records `scrollHeight` before prepend, restores via
|
||
`requestAnimationFrame`. Loading spinner shown at top of thread during fetch.
|
||
"Load older messages" button hidden while loading.
|
||
- **chat-core** bumped to v0.2.0 (new search endpoint).
|
||
- **chat** bumped to v0.2.0 (search UI, pagination polish).
|
||
|
||
## v0.5.2 — Chat Surface
|
||
|
||
### Added
|
||
|
||
- **`chat` surface package**: Full messaging UI built on `chat-core` library.
|
||
Route `/s/chat`, icon `💬`, depends on `chat-core`.
|
||
- **Conversation list sidebar**: Conversations sorted by recent activity, last
|
||
message preview, relative timestamps, unread count badges. "New" button
|
||
for conversation creation.
|
||
- **Message thread**: Paginated message history with cursor-based load-more on
|
||
scroll-to-top. Participant avatars, display names, timestamps. Scroll-to-
|
||
bottom on new messages.
|
||
- **Compose bar**: Auto-resizing textarea, Enter-to-send, Shift+Enter for
|
||
newline. Typing indicator broadcast on keypress (3s debounce).
|
||
- **Participant sidebar**: Collapsible right panel with participant list, online
|
||
status via presence hub, role badges. Add participant via `sw.ui.Dialog` +
|
||
`sw.ui.UserPicker`. Remove participant with confirmation.
|
||
- **Create conversation dialog**: Group or Direct Message type selector. Multi-
|
||
user picker with chip display for group, single picker for DM. Auto-title
|
||
from participant names.
|
||
- **Typing indicators**: Realtime broadcast via `POST /typing/:id` Starlark
|
||
endpoint. Frontend subscribes to `conversation:{id}` channel, shows
|
||
"X is typing..." with 4s auto-expire. Handles multiple typers.
|
||
- **Read receipts**: Auto mark-read on conversation focus and new incoming
|
||
messages. Unread counts in conversation list, cleared on selection.
|
||
- **Message editing**: Inline edit mode on own messages with textarea. Save on
|
||
Enter, cancel on Escape. "(edited)" label on edited messages. Realtime
|
||
propagation to other participants.
|
||
- **Message deletion**: Confirm dialog before delete. "This message was deleted"
|
||
placeholder. Admin can delete any message. Realtime propagation.
|
||
|
||
### Fixed
|
||
|
||
- **Stale token login deadlock**: REST client's 401→refresh→retry loop caused
|
||
a deadlock when the refresh endpoint itself returned 401 (stale DB). Auth
|
||
endpoints (`/auth/login`, `/auth/refresh`, `/auth/register`) are now excluded
|
||
from the retry loop. Defensive 8s timeout on `auth.boot()` ensures login
|
||
page always renders even on unexpected hangs.
|
||
- **Bundled package permissions**: Bundled packages were installed with
|
||
`pending_review` status and `granted=0` permissions, requiring manual admin
|
||
approval. Bundled installer now auto-grants all declared permissions and
|
||
sets status to `active` on install.
|
||
- **Creator display name**: `chat-core.create()` now accepts
|
||
`creator_display_name` so the conversation creator shows a readable name
|
||
in the participant sidebar instead of a raw UUID.
|
||
- **Chat dark mode theming**: Replaced non-existent CSS variable names with
|
||
actual theme tokens from `variables.css`. Fixes invisible text and wrong
|
||
backgrounds in dark mode.
|
||
- **Dialog dropdown clipping**: UserPicker autocomplete dropdown in dialogs
|
||
(New Conversation, Add Participant) no longer clipped by `overflow: auto`
|
||
on `.sw-dialog__body`.
|
||
- **User menu at scaled UI**: Menu primitive now divides anchor coordinates
|
||
and viewport dimensions by `sw.shell.getScale()` to compensate for CSS
|
||
`transform: scale()` on `#surfaceInner`. Fixes menu clipping at 110%+.
|
||
|
||
### Changed
|
||
|
||
- **Named Docker volume**: `docker-compose.yml` switched from bind mount
|
||
(`./data:/data`) to named volume (`sb_data:/data`). Fixes volume mount
|
||
failures when Docker daemon runs on a remote host (DinD, k8s pod).
|
||
Deterministic wipe via `docker compose down -v`.
|
||
|
||
## v0.5.1 — Chat Core Library
|
||
|
||
### Added
|
||
|
||
- **`chat-core` library package**: Conversations, messages, participants, and
|
||
read cursors as ext_data tables. Permissions: `db.write`, `realtime.publish`.
|
||
Consumable by other packages via `lib.require("chat-core")`.
|
||
- **4 ext_data tables**: `conversations` (title, type, created_by, updated_at),
|
||
`participants` (conversation_id, participant_id, type, display_name, role,
|
||
joined_at), `messages` (conversation_id, participant_id, content, content_type,
|
||
edited_at), `read_cursors` (conversation_id, participant_id,
|
||
last_read_message_id).
|
||
- **6 exported Starlark functions**: `create()`, `send()`, `history()`,
|
||
`add_participant()`, `remove_participant()`, `mark_read()`.
|
||
- **15 REST endpoints**: Full CRUD for conversations, messages, participants.
|
||
Cursor-based paginated message history. Per-conversation unread counts.
|
||
- **Realtime events**: `message`, `message.edited`, `message.deleted`,
|
||
`participant.added`, `participant.removed` published to
|
||
`conversation:{id}` channels.
|
||
- **`db.query()` range parameters**: New `before` and `after` kwargs accept
|
||
dicts of `{col: val}` generating `col < ?` / `col > ?` WHERE clauses.
|
||
Enables cursor-based pagination and efficient unread counting for all
|
||
extension packages. 7 new Go tests.
|
||
|
||
### Security
|
||
|
||
- **Invisible Unicode scanning gate** (GlassWorm / Trojan Source defense):
|
||
`ScanSource()` detects variation selectors, bidi overrides, zero-width chars,
|
||
tag characters, and other invisible Unicode across 10 ranges. `Verdict()`
|
||
blocks GlassWorm payloads (>=10 variation selector / tag chars), Trojan Source
|
||
(any bidi override), and excessive zero-width chars (>=6). Two enforcement
|
||
points: extension install (422 rejection before files written) and Starlark
|
||
execution (belt-and-suspenders). 23 new tests including GlassWorm payload
|
||
simulation.
|
||
|
||
### Changed
|
||
|
||
- **Admin packages type filter**: Added `Library` option alongside Surface/Extension/Full/Workflow. Filter dropdown replaced button group with themed `Dropdown` primitive (keyboard nav, CSS-var theming, consistent with rest of admin UI).
|
||
- SDK version bumped from 0.5.0 to 0.5.1.
|
||
|
||
## v0.5.0 — Realtime Primitive + Dialog Audit + Permissions UI
|
||
|
||
### Added
|
||
|
||
- **Realtime Starlark module**: `realtime.publish(channel, event, data)` lets
|
||
extensions publish events to WebSocket channels. Gated by the new
|
||
`realtime.publish` permission. Payloads limited to 7KB (pg_notify safe).
|
||
Reserved event prefixes (`system.`, `workflow.`, etc.) are blocked.
|
||
- **WS room protocol**: Clients send `room.subscribe` / `room.unsubscribe`
|
||
messages to join/leave rooms. Intercepted in the WebSocket readPump before
|
||
the bus publish gate (like ping). Per-connection cap of 100 rooms.
|
||
- **SDK realtime module**: `sw.realtime.subscribe(channel, [event], callback)`
|
||
manages room join/leave over WebSocket and filters incoming `realtime.*`
|
||
events by room. Returns unsubscribe handle. Auto re-joins all rooms on
|
||
WebSocket reconnect. `sw.realtime.channels()` debug helper.
|
||
- **Admin permissions UI**: Packages page gains a "Permissions" button for
|
||
packages with declared permissions. Inline drawer shows each permission with
|
||
Grant/Revoke toggle, granted-by user ID, and a "Grant All" bulk action.
|
||
- **Status badges**: `pending_review` (amber) and `suspended` (red) badges on
|
||
package rows. Enable toggle disabled with hint when `pending_review`.
|
||
- **SDK API client**: `permissions()`, `grantPerm()`, `revokePerm()`,
|
||
`grantAllPerms()` methods added to `sw.api.admin.packages`.
|
||
- **`starlarkToGoVal()` helper**: Converts Starlark values to Go `any` for
|
||
JSON marshaling (reverse of existing `goValToStarlark`).
|
||
- **8 new Go tests**: Route table cases for `realtime.*` and `room.*`,
|
||
realtime module publish (happy path, empty data, missing args, reserved
|
||
prefix, payload too large), `starlarkToGoVal` type conversion.
|
||
|
||
### Changed
|
||
|
||
- **Dialog audit**: 5 bare `confirm()` calls migrated to `await sw.confirm()`
|
||
with `{ destructive: true }` styling in tasks, schedules, notes (×2), and
|
||
editor packages. Package archives rebuilt.
|
||
- SDK version bumped from 0.2.3 to 0.5.0.
|
||
- Event route table: `realtime.` → DirToClient, `room.subscribe` /
|
||
`room.unsubscribe` → DirFromClient.
|
||
- Runner gains `bus` field and `SetBus()` method; wired in main.go.
|
||
|
||
## v0.4.7 — Note Graph
|
||
|
||
### Added
|
||
|
||
- **Graph API**: `GET /graph` endpoint returns all non-archived notes as nodes
|
||
and resolved wikilinks as edges in a single payload. Nodes include id, title,
|
||
folder_id, and tags. Edges include source, target, and link text.
|
||
- **Graph view**: Canvas-based force-directed graph visualization replaces the
|
||
editor pane when "Graph" button is clicked in the topbar. Force simulation
|
||
uses repulsion (all pairs), attraction (edges), and center gravity with
|
||
velocity damping. Supports zoom (scroll wheel) and pan (mouse drag).
|
||
- **Click focus**: Single-click a node to dim unconnected nodes/edges to 15%
|
||
opacity; clicked node and direct neighbors stay at full brightness. Edges
|
||
between focused nodes highlighted. Click empty space to reset.
|
||
- **Shift+click chain**: Hold shift and click a second node to union both
|
||
neighborhoods — trace thought paths across two hops.
|
||
- **Double-click open**: Double-click a graph node to navigate to that note in
|
||
the editor. Graph view closes, editor opens with the selected note.
|
||
- **Hover tooltips**: Hovering a node shows title, tag pills, and connection
|
||
count in an HTML overlay (no additional API calls).
|
||
- **Folder coloring**: Nodes colored by folder using a 10-color palette.
|
||
Unfiled notes shown in gray.
|
||
- **Tag filter integration**: Active tag filter dims non-matching nodes to 30%
|
||
opacity, stacking with click focus.
|
||
- **Orphan highlighting**: Notes with zero connections shown with dashed stroke.
|
||
"Hide orphans" checkbox in toolbar to toggle visibility.
|
||
|
||
### Changed
|
||
|
||
- Notes package version bumped from 0.7.0 to 0.8.0.
|
||
- Topbar gains a "Graph" toggle button alongside "New Note" and "Import .md".
|
||
|
||
|
||
## v0.4.6 — Sidebar Restructure
|
||
|
||
### Added
|
||
|
||
- **Sidebar tabs**: Two-tab header ("Notes" / "Outline") replaces the static
|
||
sidebar header. Notes tab contains folder tree, tag filter, search, and note
|
||
list. Outline tab shows the document heading tree for the active note.
|
||
Outline tab disabled (grayed) when no note is selected.
|
||
- **Sidebar outline**: Document outline relocated from editor-side panel into
|
||
the sidebar's Outline tab. Nested heading hierarchy (H1→H2→H3) with depth
|
||
indentation via `padding-left`. Click heading scrolls to it in both preview
|
||
and CM6 editor modes.
|
||
- **Active heading tracking**: Scroll listener (RAF-throttled) highlights the
|
||
current heading in the sidebar outline based on scroll position. Uses
|
||
`getBoundingClientRect()` in preview/split mode and CM6 viewport line in
|
||
edit mode.
|
||
|
||
### Changed
|
||
|
||
- Notes package version bumped from 0.6.0 to 0.7.0.
|
||
- `EditorPane` exposes headings, scroll function, and active heading index to
|
||
parent via callback props (`onHeadingsChange`, `scrollToHeadingRef`,
|
||
`onActiveHeadingChange`).
|
||
- Sidebar auto-resets to Notes tab when active note is deleted.
|
||
|
||
### Removed
|
||
|
||
- Old `DocumentOutline` component and `.doc-outline` CSS styles (replaced by
|
||
sidebar outline).
|
||
|
||
## v0.4.5 — Editor Modes + Document Outline
|
||
|
||
### Added
|
||
|
||
- **Tri-state view mode**: Notes open in rendered (read-only) mode by default.
|
||
Toolbar button cycles through Edit (CM6) and Split (side-by-side) modes.
|
||
Replaces the old binary preview toggle.
|
||
- **Split view**: Side-by-side CM6 editor + rendered preview using CSS grid.
|
||
CM6 instance preserved when toggling between edit and split modes.
|
||
- **Synced scroll**: In split mode, scrolling the CM6 editor proportionally
|
||
scrolls the preview pane via linear ratio mapping on `scrollDOM`.
|
||
- **View mode persistence**: User's preferred mode saved to localStorage
|
||
(`sw.storage.local('notes')`). New `editor_mode` manifest setting with
|
||
admin-level default (rendered / edit / split).
|
||
- **Document outline**: Collapsible TOC panel adjacent to the editor body.
|
||
`parseHeadings()` extracts heading level, text, and line number (skips
|
||
fenced code blocks). Click heading to scroll — uses CM6
|
||
`EditorView.scrollIntoView()` in edit/split mode, DOM `scrollIntoView()`
|
||
in rendered mode. Updates on body change with 300ms debounce.
|
||
|
||
### Changed
|
||
|
||
- Notes package version bumped from 0.5.0 to 0.6.0.
|
||
- Editor body wrapped in `.notes-editor__content` flex container to
|
||
accommodate the outline panel.
|
||
- Wikilink click handling active in both rendered and split modes.
|
||
- Mobile: split view collapses to single column, outline panel hidden.
|
||
|
||
---
|
||
|
||
## v0.4.4 — Rich Editor + Import/Export
|
||
|
||
### Added
|
||
|
||
- **CodeMirror 6 integration**: Notes editor now uses the vendored CM6 bundle
|
||
(`CM.noteEditor()`) for rich markdown editing with syntax highlighting,
|
||
wikilink autocomplete (`[[` triggers note title completion), and inline
|
||
preview decorations for headings, code blocks, and blockquotes.
|
||
- **CM6 dynamic loading**: Bundle loaded via `<script>` tag at boot time.
|
||
Falls back to plain textarea if CM6 is unavailable.
|
||
- **Export as .md**: Export button in editor header downloads the note as a
|
||
Markdown file with YAML frontmatter (title, tags, created date).
|
||
- **Import .md**: Import button in topbar allows uploading `.md`/`.markdown`/`.txt`
|
||
files. Parses YAML frontmatter for title and tags, creates note via API.
|
||
- **CSS for CM6**: Editor fills the container with `max-height: none` override
|
||
and proper scroller padding.
|
||
|
||
### Changed
|
||
|
||
- Notes package version bumped from 0.4.0 to 0.5.0.
|
||
- `EditorPane` component refactored to support CM6 with mutable refs for
|
||
title/body (required for CM6 callback closures).
|
||
- Ctrl/Cmd+S save works in both CM6 and textarea fallback modes.
|
||
|
||
---
|
||
|
||
## v0.4.3 — Backlinks + Wikilinks
|
||
|
||
### Added
|
||
|
||
- **Links table** (`ext_notes_links`): source_id, target_id, link_text columns
|
||
with indexes on source_id and target_id for bidirectional lookup.
|
||
- **Wikilink extraction**: `_extract_wikilinks()` parses `[[...]]` patterns
|
||
using `split("[[")` + `find("]]")` (Starlark has no regex or while loops).
|
||
Returns deduplicated list of link text strings.
|
||
- **Link sync on save**: `_sync_links()` called from `_create_note` and
|
||
`_update_note` when body changes. Follows the tags delete-all + reinsert
|
||
pattern. Resolves link text to note IDs via case-insensitive title matching.
|
||
Unresolved links stored with empty `target_id`.
|
||
- **Cascade delete**: Hard-deleting a note removes both outgoing links
|
||
(source_id matches) and incoming backlinks (target_id matches).
|
||
- **Link API endpoints**: `GET /links/:note_id` returns outgoing links.
|
||
`GET /backlinks/:note_id` returns incoming links enriched with source note
|
||
titles to avoid extra frontend round-trips.
|
||
- **Wikilink preview rendering**: `[[Note Title]]` syntax renders as clickable
|
||
accent-colored links in the markdown preview. Unresolved wikilinks styled
|
||
in danger/red with dashed underline.
|
||
- **Wikilink click navigation**: Clicking a resolved wikilink navigates to the
|
||
target note. Clicking an unresolved wikilink creates the note and navigates
|
||
to it, then re-saves the source note so the link resolves to blue.
|
||
- **Backlinks panel**: Collapsible panel below the editor showing all notes
|
||
that link to the current note. Each backlink displays source title and link
|
||
text. Click to navigate. Hidden when no backlinks exist.
|
||
- **Updated stats**: `/stats` response now includes `links` count.
|
||
|
||
### Data model
|
||
|
||
| Table | Columns |
|
||
|-------|---------|
|
||
| `ext_notes_links` | source_id, target_id, link_text |
|
||
|
||
### Notes package version
|
||
|
||
0.3.0 → 0.4.0
|
||
|
||
---
|
||
|
||
## v0.4.2 — Tags + Search
|
||
|
||
### Added
|
||
|
||
- **Tags table** (`ext_notes_tags`): note_id, tag columns with indexes on
|
||
both for bidirectional lookup. Many-to-many relationship via join table.
|
||
- **Tag CRUD API**: 3 new Starlark handlers — list all unique tags, get tags
|
||
for a note, replace tags for a note (delete-all + reinsert). Tags are
|
||
normalized to lowercase, trimmed, and deduplicated on save.
|
||
- **Tags in list/get/search**: `_list_notes` batch-fetches all tags in a
|
||
single query and attaches a `tags` array to each note item. `_get_note`
|
||
includes tags. `_search_notes` now matches search terms against tags.
|
||
- **Tag cascade delete**: Hard-deleting a note also removes its tag rows.
|
||
- **Tag input in editor**: `TagInput` component with removable pills, text
|
||
input (comma or Enter to add), and autocomplete dropdown populated from
|
||
all existing tags.
|
||
- **Tag pills on note cards**: `NoteCard` renders up to 3 tag pills with
|
||
"+N" overflow indicator for notes with many tags.
|
||
- **Tag filter in sidebar**: `TagFilter` component displays all unique tags
|
||
as clickable pills. Clicking toggles client-side filtering of the note
|
||
list by that tag.
|
||
- **Drag-and-drop note moves**: `NoteCard` is draggable (HTML5 drag/drop).
|
||
`FolderNode`, "All Notes", and "Unfiled" items are drop targets. On drop,
|
||
calls the existing move-note API.
|
||
- **Folder context menu**: Replaced `prompt()` hack with a proper right-click
|
||
popup menu showing "Add subfolder", "Rename", and "Delete" actions. Positioned
|
||
at click coordinates, dismissed on outside click.
|
||
- **Updated stats**: `/stats` response now includes `tags` count (unique).
|
||
|
||
### Data model
|
||
|
||
| Table | Columns |
|
||
|-------|---------|
|
||
| `ext_notes_tags` | note_id, tag |
|
||
|
||
### Notes package version
|
||
|
||
Bumped from 0.2.0 → 0.3.0. 15 API routes (12 existing + 3 new).
|
||
|
||
## v0.4.1 — Folders + Navigation Tree
|
||
|
||
### Added
|
||
|
||
- **Folders table** (`ext_notes_folders`): name, parent_id, creator_id,
|
||
sort_order. Indexed on parent_id and creator_id. Nested hierarchy via
|
||
parent_id foreign key (empty string = root).
|
||
- **Folder CRUD API**: 5 new Starlark handlers — list, create, update, delete
|
||
folders plus a dedicated move-note endpoint (`POST /notes/move`).
|
||
- **Navigation tree**: `FolderTree` + `FolderNode` Preact components in the
|
||
sidebar. Flat API response built into a nested tree client-side via `useMemo`.
|
||
Expand/collapse toggles, depth-based indentation, inline rename via
|
||
right-click context menu.
|
||
- **Folder filtering**: Clicking a folder filters notes by `folder_id` query
|
||
param. "All Notes" clears the filter. "Unfiled" shows notes with empty
|
||
folder_id (client-side filter).
|
||
- **Editor folder select**: Dropdown in the editor header moves notes between
|
||
folders. Shows nested hierarchy with `└` prefix indentation.
|
||
- **Folder-aware note creation**: New notes inherit the active folder's ID.
|
||
- **Delete cascade**: Deleting a folder orphans its notes (moves to Unfiled)
|
||
and reparents child folders to the deleted folder's parent.
|
||
- **Updated stats**: `/stats` response now includes `unfiled` and `folders`
|
||
counts.
|
||
|
||
### Data model
|
||
|
||
| Table | Columns |
|
||
|-------|---------|
|
||
| `ext_notes_folders` | name, parent_id, creator_id, sort_order |
|
||
|
||
### Notes package version
|
||
|
||
Bumped from 0.1.0 → 0.2.0. 12 API routes (7 existing + 5 new).
|
||
|
||
## v0.4.0 — Notes Surface
|
||
|
||
### Added
|
||
|
||
- **Notes surface package** (`packages/notes/`): Obsidian-style markdown notes
|
||
rebuilt as a standard installable `.pkg` archive. Type `full`, tier `starlark`.
|
||
Zero kernel changes — proves the extension stack end-to-end.
|
||
- **Starlark backend** (`script.star`): 7 API routes — CRUD, search, stats.
|
||
Note bodies stored in `ext_notes_notes` TEXT columns. List endpoint returns
|
||
lightweight projections (no body); full content fetched on select.
|
||
- **Markdown editor**: Preact+htm frontend with sidebar note list, monospace
|
||
textarea, and toggle-able live preview. Inline markdown renderer (~100 lines)
|
||
covers headings, bold, italic, code blocks, links, lists, blockquotes, and HR.
|
||
- **Auto-save**: Debounced save (1s) with dirty/saved status indicator.
|
||
`Ctrl/Cmd+S` for force save. Tab key inserts two spaces.
|
||
- **Pin & archive**: Pin important notes to the top of the list. Archive
|
||
(soft-delete) via the delete button; hard delete with `?hard=1` query param.
|
||
- **Search**: Case-insensitive full-text search across title and body content.
|
||
Search bar in sidebar filters the note list in real-time.
|
||
- **Theme support**: All styles use CSS variables — light and dark themes work
|
||
out of the box. Responsive layout collapses to vertical on narrow viewports.
|
||
|
||
### Data model
|
||
|
||
| Table | Columns |
|
||
|-------|---------|
|
||
| `ext_notes_notes` | title, body, folder_id, creator_id, updated_at, pinned, archived |
|
||
|
||
Indexes on `folder_id`, `creator_id`, `pinned`, `updated_at`.
|
||
|
||
### Planned (v0.4.1–v0.4.4)
|
||
|
||
- Folders + navigation tree
|
||
- Tags + enhanced search
|
||
- Backlinks + `[[wikilinks]]`
|
||
- Rich editor (CodeMirror 6) + markdown import/export
|
||
|
||
## v0.3.8 — Distribution
|
||
|
||
### Added
|
||
|
||
- **Bundled packages**: Production Docker image now ships with 12 pre-built
|
||
`.pkg` archives (4 workflows, 3 surfaces, 1 full, 1 library, 2 test runners,
|
||
1 extension). Auto-installed on first boot via `InstallBundledPackages()`.
|
||
Install-once, skip-if-present — admin uninstalls are respected on restart.
|
||
- **Package allowlist** (`BUNDLED_PACKAGES`): Comma-separated list of package
|
||
IDs to install. Empty (default) installs all. Useful for Helm/K8s where
|
||
different environments need different packages.
|
||
- **Skip bundled** (`SKIP_BUNDLED_PACKAGES=true`): Disables auto-install
|
||
entirely. Intended for production until post-MVP packages are ready.
|
||
- **Builder image** (`Dockerfile.builder`): Pre-caches Go modules, Node
|
||
dependencies, and vendor lib tarballs for faster custom builds.
|
||
- **Distribution docs** (`docs/DISTRIBUTION.md`): Quick start, bundled
|
||
packages, builder image usage, custom build guide, production deployment
|
||
reference.
|
||
- **Migration 012**: Adds `bundled` to `packages.source` CHECK constraint
|
||
(both Postgres and SQLite).
|
||
- **K8s manifest**: `SKIP_BUNDLED_PACKAGES` and `BUNDLED_PACKAGES` env vars
|
||
added to `k8s/armature.yaml`.
|
||
- **Tests**: 6 handler tests (fresh install, skip existing, missing dir, empty
|
||
dir, dormant handling, allowlist filtering).
|
||
|
||
### Environment defaults
|
||
|
||
| Environment | `SKIP_BUNDLED_PACKAGES` | `BUNDLED_PACKAGES` |
|
||
|-------------|------------------------|--------------------|
|
||
| Dev (compose) | `false` | (empty = all) |
|
||
| Test (K8s) | `false` | (empty = all) |
|
||
| Prod (K8s) | `true` | — |
|
||
|
||
## v0.3.7 — Package Audit
|
||
|
||
### Added
|
||
|
||
- **Dormant status**: New `dormant` value in `packages.status` CHECK constraint
|
||
(both Postgres and SQLite). Installer auto-detects packages with unmet
|
||
`requires` entries and sets `status=dormant, enabled=false`. Enable endpoint
|
||
returns 409 for dormant packages.
|
||
- **Generalized requires check**: Any unmet requirement triggers dormant status,
|
||
not just specific values. Known capabilities checked against a whitelist;
|
||
anything unknown is unmet.
|
||
- **Admin UI indicators**: Dormant badge, disabled Enable button with tooltip,
|
||
Dormant stat card in packages admin view.
|
||
- **Tests**: 4 new handler tests covering dormant status, enable blocking,
|
||
surface exclusion, and admin list inclusion.
|
||
|
||
### Fixed
|
||
|
||
- **Manifest fixes**: Fixed 6 chat-extension manifests (`"name"` → `"title"`)
|
||
that were blocking install. Added explicit `"type": "surface"` to
|
||
hello-dashboard, icd-test-runner, sdk-test-runner.
|
||
- **Dashboard + Editor tagged dormant**: Both depend on removed `sw.*`
|
||
imperative SDK (`sw.tabs`, `sw.chat`, `sw.layout`, etc.) — render blank.
|
||
Tagged with `requires: ["legacy-sdk"]` so installer auto-sets dormant.
|
||
- **Dependency check**: Relaxed library dependency validation to allow
|
||
`pending_review` libraries (gitea-client declares permissions). Only
|
||
`suspended`/`dormant` libraries blocked.
|
||
|
||
### Package Audit Results
|
||
|
||
- **7 working surfaces**: schedules, tasks, team-activity-log, git-board,
|
||
hello-dashboard, icd-test-runner, sdk-test-runner
|
||
- **8 dormant**: 6 chat-dependent (csv-table, diff-viewer, js-sandbox,
|
||
katex-renderer, mermaid-renderer, regex-tester) + 2 legacy-sdk (dashboard,
|
||
editor)
|
||
- **1 library**: gitea-client (standalone, active)
|
||
|
||
## v0.3.6 — Example Workflows + Interactive Demo
|
||
|
||
### Added
|
||
|
||
- **Bug Report Triage** (`packages/bug-report-triage`): Public entry, progressive
|
||
fieldsets, severity-based branch routing, SLA timer (3600s on critical fix).
|
||
- **Employee Onboarding** (`packages/employee-onboarding`): Starlark automated
|
||
stages (`db.insert`, `notifications.send`), manager signoff gate with
|
||
`required_role`, rejection reroute. `script.star` with `on_provision` and
|
||
`on_welcome` hooks. Creates `provisions` and `onboarding_log` ext_data tables.
|
||
- **Content Approval** (`packages/content-approval`): Multi-party signoff (quorum
|
||
of 2), rejection reroute creating a review cycle loop.
|
||
- **Webhook Notifier** (`packages/webhook-notifier`): Starlark `http.post` with
|
||
`connections.get` fallback, delivery logging to `webhook_log` ext_data table.
|
||
- **Workflow Demo** surface (`packages/workflow-demo`): Interactive walkthrough
|
||
with workflow cards, stage flow diagrams, Starlark viewer, API curl examples,
|
||
and "Try It" buttons. Route: `/s/workflow-demo`.
|
||
- **Engine context fix**: `started_by` added to automated stage Starlark context
|
||
dict, enabling hooks to reference the workflow initiator.
|
||
- **SLA package installer**: `sla_seconds` field added to `workflowPkgStage`
|
||
struct and wired in both install and export paths.
|
||
- **Snapshot format fix**: `parseSnapshotStages` helper handles both wrapped
|
||
(`{"stages":[...]}`) and legacy flat array snapshot formats in the engine,
|
||
fixing `corrupt version snapshot` errors when starting published workflows.
|
||
- **Workflow adoption**: `POST /teams/:teamId/workflows/:id/adopt` claims a
|
||
global (team_id=NULL) workflow for a team. `GET /teams/:teamId/workflows/available`
|
||
lists adoptable workflows. `TeamID` added to `WorkflowPatch` model.
|
||
- **Tests**: 3 new engine tests (automated context, SLA install, manifest
|
||
roundtrip). Total: 142 handler tests passing.
|
||
- Per-package `README.md` files for all 5 new packages.
|
||
|
||
### Fixed
|
||
|
||
- **Demo surface**: Added `sw.userMenu` to topbar. Fixed workflow install
|
||
detection (was reading wrong response key). CSS uses theme variables for
|
||
dark mode support.
|
||
- **Admin teams tab**: Extracted `.data` from paginated response — was stuck
|
||
at loading because `setTeams` received the envelope object, not the array.
|
||
- **Team-admin workflows**: Added "Adopt Global" button to claim package-installed
|
||
workflows into a team scope. Workflow list now unwraps paginated responses.
|
||
|
||
## v0.3.5 — Settings Audit + ICD + Tests + Clone
|
||
|
||
### Added
|
||
|
||
- **Clone endpoint**: `POST /api/v1/workflows/:id/clone` deep-copies a workflow
|
||
and all its stages. Creates an inactive draft with "Copy of" prefix. Handles
|
||
slug collisions with auto-suffix. Team-scoped mirror at
|
||
`/api/v1/teams/:teamId/workflows/:id/clone`.
|
||
- **Integration tests**: 7 new engine-level tests in `workflow_engine_test.go`
|
||
covering full lifecycle, branch routing, public entry, signoff validation gate,
|
||
signoff rejection, cancel-clears-assignments, and error cases. Total: 28 tests.
|
||
- **Staleness timeout UI**: `staleness_timeout_hours` input added to workflow
|
||
editor in team-admin settings (was backend-only since v0.3.3).
|
||
- **Branch rules UI**: Collapsible JSON textarea in stage form for configuring
|
||
conditional routing rules (was backend-only since v0.3.2).
|
||
|
||
### Changed
|
||
|
||
- **ICD (OpenAPI spec)**: Fixed stale `Workflow` schema (added `description`,
|
||
`branding`, `is_active`, `on_complete`, `retention`, `staleness_timeout_hours`).
|
||
Fixed stale `Stage` schema (removed `history_mode`/`transition_rules`, updated
|
||
`stage_mode` enum to `[form, review, delegated, automated]`, added `audience`,
|
||
`stage_type`, `starlark_hook`, `branch_rules`, `stage_config`, `sla_seconds`,
|
||
`auto_transition`, `assignment_team_id`, `surface_pkg_id`).
|
||
Added `WorkflowInstance`, `WorkflowAssignment`, `WorkflowSignoff` schemas.
|
||
Added ~20 new endpoint paths: instances, assignments, signoffs, public entry,
|
||
clone, and team roles. Added tags: Workflow Instances, Workflow Assignments,
|
||
Workflow Signoffs, Public Workflows.
|
||
|
||
## v0.3.4 — Team Roles + Multi-party Validation
|
||
|
||
### Added
|
||
|
||
- **Custom team roles**: Removed `CHECK (role IN ('admin','member'))` constraint
|
||
from `team_members` (both Postgres and SQLite). Custom roles are stored in
|
||
`teams.settings["roles"]` as a JSON array. Builtins `admin` and `member` are
|
||
always present.
|
||
- **Team roles API**: `GET /api/v1/teams/:teamId/roles` returns configured roles.
|
||
`PUT /api/v1/teams/:teamId/roles` replaces the roles array (builtins enforced).
|
||
- **Role-based stage assignment**: `stage_config.required_role` restricts which
|
||
team members can claim an assignment. `CheckClaimRole` verifies the user's team
|
||
membership role before allowing claim; rolls back on mismatch (403).
|
||
- **Multi-party sign-off**: New `workflow_signoffs` table with
|
||
`UNIQUE(instance_id, stage, user_id)` preventing double-signing.
|
||
`StageConfig.validation` supports `required_approvals`, `required_role`, and
|
||
`reject_action` (cancel or reroute to named stage).
|
||
- **Validation gate**: `advanceInternal` blocks stage advancement until the
|
||
required number of approvals is met. Rejections trigger cancel or reroute
|
||
based on `reject_action`.
|
||
- **`Engine.SubmitSignoff`**: Records approval/rejection, enforces signoff role
|
||
if configured, emits `workflow.signoff` event.
|
||
- **Signoff HTTP API**: `POST /api/v1/instances/:iid/signoffs` (submit),
|
||
`GET /api/v1/instances/:iid/signoffs` (list current stage signoffs).
|
||
Team-scoped mirrors at `/api/v1/teams/:teamId/instances/:iid/signoffs`.
|
||
- **New events**: `workflow.signoff`, `workflow.rejected`.
|
||
- **Frontend — Members page**: Dynamic role selects populated from team roles API.
|
||
"Manage Roles" panel for adding/removing custom roles inline.
|
||
- **Frontend — Stage editor**: "Required Role (claim)" dropdown and "Multi-party
|
||
Validation" section (required approvals, signoff role, on reject action) appear
|
||
when a team is selected for the stage.
|
||
- **Frontend — Monitor tab**: Signoff panel with approve/reject buttons, comment
|
||
field, and approval progress display.
|
||
- **3 new store tests**: CreateSignoff (with UNIQUE constraint check),
|
||
ListSignoffs (ordering), CountSignoffs (decision filter). 20 total, all passing.
|
||
- **Design doc**: `docs/DESIGN-EXTENSION-LIFECYCLE.md` — permanent vs PoC
|
||
packages, graduation criteria, explicit install model.
|
||
- **Design doc**: `docs/DESIGN-TRIGGER-COMPOSITION.md` — triggers/schedules can
|
||
start workflows, workflows emit events, no circular invocation.
|
||
|
||
### Changed
|
||
|
||
- `addMemberRequest` and `updateMemberRequest` binding relaxed from
|
||
`oneof=admin member` to `required,min=1,max=50` with application-level
|
||
validation against the team's configured roles.
|
||
- `TruncateAll` test helper updated to include `workflow_signoffs`.
|
||
|
||
---
|
||
|
||
## 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.2 — Workflow Engine
|
||
|
||
### Added
|
||
|
||
- **Stage execution engine**: `server/workflow/engine.go` — `Start`, `Advance`
|
||
(with `branch_rules` conditional routing), and `Cancel` operations. Merges
|
||
`stage_data` across stages, creates assignments, emits lifecycle events.
|
||
- **Automated stage handler**: `server/workflow/automated.go` — fires Starlark
|
||
hook, auto-advances on success, cycle guard (max 10 consecutive automated
|
||
stages).
|
||
- **Instance HTTP API**: Start, GetInstance, Advance, Cancel, ListInstances.
|
||
Team-scoped mirrors under `/api/v1/teams/:teamId/workflows/`.
|
||
- **Assignment HTTP API**: Claim, Unclaim, Complete, Cancel, ListByTeam,
|
||
ListMine. Claimer identity verification on unclaim/complete.
|
||
- **Starlark module expansion**: `workflow.get_instance()`,
|
||
`workflow.list_instances()` (read-only).
|
||
- **14 store tests** covering all 15 v0.3.1 methods (instance + assignment
|
||
CRUD). All passing.
|
||
|
||
---
|
||
|
||
## v0.3.1 — Instance Assignment
|
||
|
||
### Added
|
||
|
||
- **`workflow_instances` table**: Tracks workflow execution state —
|
||
`workflow_version`, `current_stage`, `stage_data`, `status`, `entry_token`.
|
||
- **`workflow_assignments` table**: Per-stage queue for instance assignments —
|
||
`instance_id`, `stage`, `team_id`, `assigned_to`, `status`, `review_data`.
|
||
Optimistic claim locking via status transition.
|
||
- **15 new store methods**: Full CRUD for instances (Create, Get, GetByToken,
|
||
Update, List, AdvanceStage, Complete, Cancel) and assignments (Create, Claim,
|
||
Unclaim, Complete, Cancel, ListByTeam, ListByInstance, ListByUser).
|
||
- **New events**: `workflow.started`, `workflow.cancelled`, `workflow.error`.
|
||
|
||
---
|
||
|
||
## 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 no
|
||
longer exist in the kernel.
|
||
- **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 reflect current surface architecture.
|
||
|
||
### Removed
|
||
|
||
- **~500 lines of dead CSS**: Orphaned classes for removed legacy UI
|
||
components (chat, channel, project, notes, editor-chat, sidebar,
|
||
router-picker) 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 surface links; menu is now fully dynamic.
|
||
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-Armature-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
|
||
|
||
Initial release. Pure extension platform — kernel provides auth, RBAC,
|
||
storage, and the Starlark sandbox. Everything else is a package.
|
||
|
||
### 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` → `armature-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 Armature" → "Armature", updated tagline
|
||
and feature pills to reflect platform pivot
|
||
|
||
### Changed
|
||
|
||
- Go module: `armature`
|
||
- VERSION: `0.1.0`
|
||
- Default DB name: `armature`
|
||
- 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 "Armature"
|
||
- 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)
|