# Changelog All notable changes to Armature are documented here. ## 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.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 `` 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 `