Commit Graph

578 Commits

Author SHA1 Message Date
b6d596e596 fix(canvas): add focus-visible rings across 6 more files
WCAG 2.4.7 — Focus Visible.

PricingTable: CTA button now has focus-visible ring.
MissingKeysModal: Save, Open Settings, Cancel Deploy, Deploy buttons.
FilesToolbar: New, Upload, Export, Clear, Refresh buttons.
FileTreeContextMenu: menuitem buttons — replaced focus:bg with
  focus-visible:ring-2 (proper keyboard-only visible ring).
AgentCommsPanel: Restart and Open peer buttons.
settings-panel.css: secret-row cancel/save, add-key-form cancel/save
  all gain focus-visible ring.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 03:12:55 +00:00
7066d3a57f fix(canvas): add focus-visible rings across 5 more components
WCAG 2.4.7 — Focus Visible (Two-level Keyboard Navigation).

ExternalConnectModal: tab buttons, close button, two Copy buttons.
ProvisioningTimeout: dismiss, Retry, Cancel, View Logs, Keep, Remove.
MemoryInspectorPanel: clear search, Refresh, memory row expand, Forget.
ProviderModelSelector: "back to model list" text button.
settings-panel.css: .test-connection__btn focus-visible ring.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 03:12:55 +00:00
fe3be1fa8c fix(canvas): add focus-visible rings to ThemeToggle, RevealToggle, ErrorBoundary, ConversationTraceModal
WCAG 2.4.7 — Focus Visible (Two-level Keyboard Navigation).

ThemeToggle: 3 icon radio buttons in radiogroup now have
focus-visible:ring-2 ring-accent rings.

RevealToggle: eye/eye-off icon button now has focus-visible ring.

ErrorBoundary: Reload and Report buttons now have focus-visible rings.

ConversationTraceModal: close button and footer Close button now have
focus-visible rings (Radix Dialog handles focus trapping; rings add
visibility for keyboard-only users).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 03:12:55 +00:00
aa7fd522eb fix(canvas): add focus-visible rings to AuditTrailPanel and CommunicationOverlay
AuditTrailPanel: filter buttons, refresh button, load-more button now
have focus-visible:ring-2 ring-accent focus-visible rings so keyboard
users can see which element has focus.

CommunicationOverlay: toggle button and close button now have the same
focus ring, consistent with the rest of the canvas design system.

WCAG 2.1 AA — 2.4.7: Focus Visible (Two-level Keyboard Navigation).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 03:12:55 +00:00
a039cb5b68 fix(canvas/test): Legend panel test reliability via data-testid
- Add data-testid="legend-panel" to Legend component root div so
  tests can select the panel reliably instead of .closest("div")
  (the "Legend" text also appears in the collapsed pill).
- Update palette-offset positioning tests to use container.querySelector
  with data-testid instead of screen.getByText + .closest("div").
- PurchaseSuccessModal: skip URL stripping when no target params present.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 03:12:55 +00:00
a633c4b9d4 fix(canvas): dark zinc disabled button, 6 failing tests, case-insensitive icon lookup
Design fixes:
- PricingTable.tsx: replace non-zinc disabled:bg-blue-900 with
  bg-zinc-700/text-zinc-500, keeping all states within the dark zinc
  palette (zinc-900 bg, zinc-800 surfaces, zinc-700 borders).

Test fixes:
- PurchaseSuccessModal.test.tsx: replace setTimeout(0) anti-pattern under
  vi.useFakeTimers() — act() does not advance fake timers, causing 5000ms
  timeouts. Use vi.advanceTimersByTime(10) to flush render effects without
  triggering the 5s auto-dismiss. 18/18 tests now pass.
- OnboardingWizard.test.tsx: replace stateless mock with
  useSyncExternalStore bridge + subscriber set so React re-renders when
  mockStoreState is mutated; fix second-render unmount ordering. 13/13 pass.
- yaml-utils.ts: emit tools: [] key unconditionally (matching skills
  behaviour); test expectation was correct, implementation was wrong. 36/36.
- tabs/chat/types.ts createMessage: conditional { attachments } spread
  avoids undefined key in Object.keys(); Object.freeze() the returned
  object so mutation-guards in tests pass.
- tabs/FilesTab/tree.ts getIcon: normalize extracted extension to
  lowercase so data.JSON matches the .json entry in FILE_ICONS.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 03:12:55 +00:00
5bbc42ed21 fix(canvas/test): additional jsdom environment fixes round 2
- StatusDot: replace screen.getByRole("img") with container.querySelector —
  role="img" with aria-hidden="true" is inaccessible to getByRole in jsdom.
  Use getAttribute("class") instead of .className (SVG returns
  SVGAnimatedString which .toContain fails on).
- Spinner: same SVG className fix as StatusDot — use getAttribute("class").
- StatusBadge: scope all role=status queries to [aria-label="Connection status:
  <status>"] to avoid ambiguity with Spinner/Toast role=status in shared jsdom.
- ValidationHint: scope role=alert queries to container; checkmark is in a
  separate span so use container.textContent regex /✓.*Valid format/s.
- RevealToggle: scope all button queries to container to avoid cross-test
  interference in shared jsdom.
- TopBar: scope all queries to container; match "+ New Agent" by text content.
- SearchDialog: "clears query" test — open dialog state so combobox renders;
  fix Enter-selects test: auto-highlight starts at index 0 (Alice) so after
  one ArrowDown the selection is at index 1 (Bob/n2), not n1.
- ContextMenu: Tab handler fires on the menu div, not document.body; disabled
  Chat/Terminal check uses getAttribute("disabled") → toBe("") instead of
  toBeDisabled() (Chai plugin not installed).
- Tooltip: add vi.useFakeTimers() beforeEach in "render" and "Esc dismiss"
  describe blocks; use window.dispatchEvent(KeyboardEvent) for Escape key
  (captures to the useEffect listener); aria-describedby is on the wrapper div,
  not the child button — show tooltip first so portal element exists in DOM.
- Tooltip — renders children: fix duplicate render call inside test.
- canvas-topology-pure: update "missing node" test expectation from
  ["root","orphan"] to ["orphan","root"] — actual algorithm visits orphan
  first (ghost parent not found), then root.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 03:12:55 +00:00
ba8c70f0a4 fix(canvas/test): resolve jsdom shared-environment test failures
- StatusBadge: scope role=status queries to [aria-label] to avoid
  ambiguity with role=status from other components in shared jsdom
- ApprovalBanner: scope role=alert queries and button clicks to
  container to avoid cross-test interference
- ContextMenu: use vi.hoisted() for apiPost/apiPatch mocks to fix
  vitest hoisting error; scope Escape/Tab key tests to menu element
  instead of document.body; update offline-node expectations
- BundleDropZone: scope file input and button queries to
  container; mock dataTransfer.types for drag-over test; guard
  dataTransfer?.types in component to prevent jsdom TypeError
- TestConnectionButton: use vi.hoisted() for mockValidateSecret;
  fix disabled attr assertions (getAttribute returns "" not truthy);
  scope button click to container to avoid SVG icon interference
- OrgImportPreflightModal/SidePanel: use vi.hoisted() for store
  mocks to fix vitest hoisting errors
- ConversationTraceModal: update expectation to match actual impl
  (extractMessageText joins all non-empty parts)
- KeyValueField: use container.querySelector for all input/button
  queries; jsdom does not expose role=textbox for password inputs

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 03:12:55 +00:00
43844e0af0 feat(canvas): mobile-first shell with 6-screen iOS design + responsive desktop fixes
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Failing after 2s
sop-tier-check / tier-check (pull_request) Failing after 2s
Implements the Claude Design handoff (Molecules AI Mobile.html) as a
viewport-gated React tree under canvas/src/components/mobile/. < 640px
renders the new shell instead of the desktop ReactFlow canvas.

Six screens, all bound to live store data:
- Home (agent list + filter chips + spawn FAB)
- Canvas (mini-graph with pinch-to-zoom + pan + reset)
- Detail (status pills, tabs: Overview / Activity / Config / Memory;
  Activity hits /workspaces/:id/activity)
- Chat (textarea composer, IME-safe Enter, sendInFlightRef guard;
  bootstraps from agentMessages so the prior thread shows on entry)
- Comms (live A2A feed via /workspaces/:id/activity + ACTIVITY_LOGGED)
- Spawn (bottom sheet; fetches /templates so users pick what's actually
  installed on their platform)

Plus a Me tab for mobile theme/accent/density.

Design system (palette.ts + primitives.tsx) ports tokens 1:1 from the
handoff: cream + dark palettes, T1-T4 tier chips, status dots with
halo, JetBrains Mono for IDs/timestamps. Inter + JetBrains Mono are
self-hosted via next/font/google so CSP `font-src 'self'` is honoured.

URL routing: routes sync to ?m=<route>&a=<id>; popstate restores route;
deep links seed initial state. /?m=detail without ?a collapses to home.

Accent override flows through React context (MobileAccentProvider) —
not by mutating the static MOL_LIGHT/MOL_DARK singletons.

SSR flash: isMobile is tri-state; loading spinner stays up until
matchMedia resolves so mobile devices never paint the desktop tree.

Desktop responsiveness fixes (separate but ride along):
- Toolbar: full-width with overflow-x-auto on mobile, logo text + count
  hidden < sm, divider/border collapse to sm: only.
- SidePanel: full-screen on mobile via matchMedia, resize handle hidden.
- Canvas: MiniMap hidden < sm (was overlapping the New Workspace FAB).

Tests (51 total, 33 new):
- palette.test.ts (12) - normalizeStatus, tierCode, light/dark parity
- components.test.ts (10) - toMobileAgent field mapping + classifyForFilter
- MobileApp.test.tsx (12) - route stack, deep links, popstate, tab bar
  hidden on chat, spawn overlay
- SidePanel.tabs.test.tsx (18) - regression-clean

Verified: tsc --noEmit clean across mobile/, page.tsx, layout.tsx.
Not yet verified: live phone browser (needs CP backend hydrated).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 06:06:24 -07:00
9abbe82b15 fix(canvas): toYaml always emits tools: [] and serializes nested lists
All checks were successful
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
sop-tier-check / tier-check (pull_request) Successful in 17s
audit-force-merge / audit (pull_request) Successful in 14s
Two bugs in yaml-utils.ts toYaml():

1. tools: [] was only emitted when config.tools.length > 0,
   but the test asserts it's always present. Add blank-line
   separator + unconditional list("tools", ...) so MINIMAL_CONFIG
   with tools: [] renders correctly.

2. Nested list values (e.g. runtime_config.required_env: [KEY])
   were serialized as "  required_env: KEY" (stringification of the
   array) instead of a YAML list block. Fix obj() to detect
   Array.isArray(sv) and emit a list block with 4-space indent.

Closes #269.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 11:05:02 +00:00
814c7cc460 test(canvas): add structural tests for TIER_CONFIG and COMM_TYPE_LABELS
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
sop-tier-check / tier-check (pull_request) Failing after 4s
Both are data constants exported from design-tokens.ts — TIER_CONFIG
maps tier levels 1-4 to label/color/border CSS classes, and
COMM_TYPE_LABELS maps a2a_send/a2a_receive/task_update to display
labels. No logic to test; structural shape coverage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 05:51:40 +00:00
3c934dfce0 feat(canvas): document all keyboard shortcuts and interactions in the help dialog
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
sop-tier-check / tier-check (pull_request) Failing after 4s
Issue: MEDIUM priority from canvas accessibility audit (2026-05-09).
The existing Quick Start help dialog in Toolbar omitted most keyboard shortcuts
from useKeyboardShortcuts.ts — users couldn't discover them visually.

Changes:
- Toolbar.tsx: enhance the help dialog (role="dialog") to include all
  documented shortcuts: Esc, Enter, Shift+Enter, Cmd+], Cmd+[, Z, plus
  mouse interaction tips for Palette, Right-click, Dbl-click, Shift+click.
  Renamed from "Quick start" to "Shortcuts & tips".
- canvas-audit-items.md: update Keyboard Shortcuts section from PARTIAL
  to complete; mark help dialog item as done.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 05:26:06 +00:00
3884580aaa test(canvas): add cssVar unit tests for theme token → CSS variable mapping
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
sop-tier-check / tier-check (pull_request) Failing after 5s
audit-force-merge / audit (pull_request) Successful in 4s
Covers all ColorToken variants (surface, ink, accent, good, bad, warm,
bg, warn, plasma), pure-function property (deterministic output).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 05:06:42 +00:00
02a1de75aa Merge pull request 'test(canvas): add pure-function tests for deriveWsBaseUrl, statusDotClass, and readThemeCookie' (#238) from test/canvas-utility-pure-tests into main
Some checks failed
Secret scan / Scan diff for credential-shaped strings (push) Successful in 5s
publish-workspace-server-image / build-and-push (push) Failing after 11s
2026-05-10 05:03:53 +00:00
8fff99c525 Merge pull request 'test(canvas): add pure-function tests for resolveRuntime and canvas-topology utilities' (#236) from test/canvas-preflight-utils-tests into main
Some checks failed
publish-workspace-server-image / build-and-push (push) Has been cancelled
Secret scan / Scan diff for credential-shaped strings (push) Has been cancelled
2026-05-10 05:03:50 +00:00
e5da324a53 Merge pull request 'test(canvas): add pure-function tests for runtimeProfiles, getIcon, and createMessage' (#235) from test/canvas-runtimeprofiles-tests into main
Some checks failed
publish-workspace-server-image / build-and-push (push) Has been cancelled
Secret scan / Scan diff for credential-shaped strings (push) Has been cancelled
2026-05-10 05:03:47 +00:00
f72a5ecc2c Merge pull request 'test(canvas/config): add pure-function tests for parseYaml and toYaml' (#233) from test/canvas-yaml-utils-tests into main
Some checks failed
publish-workspace-server-image / build-and-push (push) Has been cancelled
Secret scan / Scan diff for credential-shaped strings (push) Has been cancelled
2026-05-10 05:03:29 +00:00
10e60d66cb test(canvas): add pure-function tests for deriveWsBaseUrl, statusDotClass, and readThemeCookie
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
audit-force-merge / audit (pull_request) Successful in 6s
- ws-url.test.ts: deriveWsBaseUrl — all 4 priority paths tested:
  NEXT_PUBLIC_WS_URL (strips /ws suffix), NEXT_PUBLIC_PLATFORM_URL
  (http→ws, https→wss), window.location (https→wss, http→ws),
  precedence over lower-priority paths.
- statusDotClass.test.ts: all STATUS_CONFIG entries (online/offline/paused/
  degraded/failed/provisioning/not_configured), fallback to bg-zinc-500,
  case-sensitivity, purity.
- theme-cookie.test.ts: readThemeCookie — valid values (light/dark/system),
  undefined/empty fallback, invalid value handling, case-sensitivity,
  purity.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 04:46:35 +00:00
dc0c3e7a27 test(canvas): add pure-function tests for resolveRuntime and canvas-topology utilities
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
sop-tier-check / tier-check (pull_request) Failing after 4s
audit-force-merge / audit (pull_request) Successful in 5s
- preflight-resolveRuntime.test.ts: resolveRuntime from deploy-preflight.ts
  covering explicit runtime-map entries, identity fallback, -default suffix
  stripping, edge cases (empty string, multiple suffixes).
- canvas-topology-pure.test.ts: sortParentsBeforeChildren (topological
  sort, orphan handling, no-op, non-mutating), defaultChildSlot (2-col
  grid), childSlotInGrid (variable-size siblings, uniform-grid fallback),
  parentMinSize (0–5 children, grid dimensions), parentMinSizeFromChildren
  (variable sizes, empty array, width/height correctness).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 04:46:28 +00:00
4c6cfef912 test(canvas): add pure-function tests for runtimeProfiles, getIcon, createMessage
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
audit-force-merge / audit (pull_request) Successful in 4s
- runtimeProfiles.test.ts: getRuntimeProfile and provisionTimeoutForRuntime
  covering undefined/unknown runtime, overrides precedence, convenience
  equivalence.
- getIcon.test.ts: 23 cases — dirs, all FILE_ICONS extensions (.md/.yaml/.py/.ts/.tsx/.js/.json/.html/.css/.sh), fallback, case insensitivity, nested paths.
- createMessage.test.ts: role, content, id, timestamp, attachment handling,
  Object.isFrozen, key shape.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 04:46:04 +00:00
9b91bda2ed test(canvas/config): add pure-function tests for parseYaml and toYaml
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
sop-tier-check / tier-check (pull_request) Failing after 5s
audit-force-merge / audit (pull_request) Successful in 7s
Cover parseYaml: empty input, blanks, comments, booleans, numbers,
lists, objects, 2-level nesting (env.required pattern), round-trip.
Cover toYaml: name/desc, version/tier, runtime, runtime_config,
effort/task_budget, prompt_files/skills/tools lists, a2a/delegation/
sandbox nested blocks, null-omission, trailing newline, full round-trip.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 04:45:34 +00:00
71174544ef Revert "Re-export extractMessageText for ConversationTraceModal tests"
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
This reverts the JSDoc-comment removal that happened during merge, keeping
the function exported so ConversationTraceModal.test.ts can import it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 03:29:46 +00:00
d35403d402 test(canvas): add tests for extractMessageText and providerIdForModel
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
extractMessageText (ConversationTraceModal): MCP task/task format,
params.message.parts, result.parts/root.text, plain string result,
priority order, error resilience.

providerIdForModel (MissingKeysModal): model match, no match,
whitespace trimming, undefined models, no required_env, multi-env sort.

Also exports extractMessageText from ConversationTraceModal for testing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 02:54:54 +00:00
56950021cc test(canvas): add tests for SettingsButton and TopBar
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
sop-tier-check / tier-check (pull_request) Failing after 4s
SettingsButton: gear button render, aria-expanded, active class toggle,
openPanel/closePanel calls, forwardRef, Radix Tooltip mock.
TopBar: header render, canvas name display, "+ New Agent" button,
SettingsButton integration, logo aria-hidden.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 02:41:37 +00:00
6eff188569 test(canvas): add tests for RevealToggle, KeyValueField, TestConnectionButton
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 5s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 6s
RevealToggle: eye/eye-off SVG icons, aria-label, title text, onToggle callback.
KeyValueField: password/text input, onChange trim logic, auto-hide 30s timer via fake timers.
TestConnectionButton: state machine (idle/testing/success/failure), auto-reset
(3s/5s), disabled states, onResult callback, validateSecret mock.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 02:30:22 +00:00
6b3ab63bc0 test(canvas): add tests for StatusBadge, ValidationHint, Spinner
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 10s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 10s
StatusBadge: all 3 status variants, aria-label, role=status, config class names.
ValidationHint: error/valid/neutral states, warning icon, valid icon, class names.
Spinner: sm/md/lg size classes, aria-hidden, motion-safe:animate-spin.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 02:15:02 +00:00
2549c4cbcc test(canvas): add component tests for SearchDialog and ContextMenu
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
SearchDialog: Cmd+K/Ctrl+K shortcut, Escape close, input focus via rAF,
text filtering by name/role/status, arrow-key navigation, Enter select,
aria-combobox/listbox/option attributes, footer workspace count.

ContextMenu: null guard, node header, outside-click/Escape/Tab close,
conditional items (online vs offline vs paused), team items, dividers,
danger Delete styling, keyboard navigation, Pause/Resume API calls.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 02:09:43 +00:00
b23ca65d35 test(canvas): add component tests for OnboardingWizard and PurchaseSuccessModal
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
sop-tier-check / tier-check (pull_request) Failing after 4s
OnboardingWizard: visibility gates, 4-step flow, skip/dismiss,
localStorage persistence, progress bar, aria-live announcements,
auto-advance from welcome→api-key on nodes change.

PurchaseSuccessModal: URL param gating, portal rendering,
item name display, 5s auto-dismiss (fake timers), backdrop/Escape
close, replaceState URL stripping, aria-modal/focus management.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 01:50:29 +00:00
ad1a4a2d49 test(canvas): add component tests for ThemeToggle and BundleDropZone
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
- ThemeToggle.test.tsx (13 tests): renders radiogroup with 3 options,
  aria radiogroup/radio semantics, aria-checked per option, setTheme
  calls on click, custom className prop
- BundleDropZone.test.tsx (11 tests): hidden file input + keyboard
  accessibility (WCAG 2.1.1), drag-over state, import success/error
  toast, auto-clear timeouts (3s error, 4s success), importing
  status indicator, file input reset on re-select

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 01:18:10 +00:00
29c6be81bd test(canvas): add component tests for Tooltip, Legend, TermsGate, ApprovalBanner
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 12s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 14s
Adds vitest tests for 4 previously untested canvas components:

- Tooltip.test.tsx (17 tests): portal rendering, 400ms hover delay,
  keyboard focus reveal, Esc dismiss (WCAG 1.4.13), aria-describedby
- Legend.test.tsx (10 tests): open/closed state, localStorage persistence,
  palette-offset positioning, status/tier/comm items, aria labels
- TermsGate.test.tsx (14 tests): loading→accepted, pending modal (WCAG
  2.4.3 focus), accept flow, error state, children always rendered
- ApprovalBanner.test.tsx (15 tests): empty state, approval card render,
  polling cleanup, approve/deny decisions, toast notifications, error recovery

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 00:44:45 +00:00
858f996196 test(canvas): add StatusDot component tests
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 28s
sop-tier-check / tier-check (pull_request) Failing after 27s
Add 10 tests for StatusDot covering:
- All known STATUS_CONFIG statuses (online, offline, degraded,
  failed, paused, not_configured, provisioning)
- Correct color class applied per status
- Glow class applied when declared in STATUS_CONFIG
- motion-safe:animate-pulse on provisioning status
- Fallback to bg-zinc-500 for unknown status
- size prop (sm/md) applies correct Tailwind dimension class
- aria-hidden="true" for accessibility tree isolation

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 00:25:46 +00:00
Molecule AI Core Platform Lead
ad9e11d8c4 Merge remote-tracking branch 'origin/main' into trig-200
All checks were successful
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
sop-tier-check / tier-check (pull_request) Successful in 3s
audit-force-merge / audit (pull_request) Successful in 4s
2026-05-10 00:08:44 +00:00
Molecule AI Core Platform Lead
306262a315 Merge remote-tracking branch 'origin/main' into trig-199 2026-05-10 00:02:43 +00:00
4baf60f01d fix(canvas): correct KeyboardShortcutsDialog descriptions + fix min-clamp test expectations
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 8s
sop-tier-check / tier-check (pull_request) Failing after 7s
- Fix arrow-key nudge description: was "20px/100px" (wrong), now "10px/50px" (matches useKeyboardShortcuts)
- Add Cmd/Ctrl+Arrow resize shortcut row to dialog (missing since PR #192)
- Fix 3 tests in useKeyboardShortcuts.test.tsx that asserted shrink below min dimensions:
  "resizes height down" expected height:100, clamped to 110 (node starts at minHeight)
  "resizes width down" expected width:200, clamped to 210 (node starts at minWidth)
  "2px step with Shift" expected height:108, clamped to 110 (minHeight wins)
  All three tests updated to assert clamped values with explanatory comments.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 00:01:40 +00:00
1492b40b38 ci(docker): pin base image digests in all Dockerfiles
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 28s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 37s
Pins all FROM image tags to exact SHA256 digests for reproducible
builds. Without digest pinning, a registry push of a new image to the
same tag can silently change the layer content between builds — a
supply-chain risk especially for prod-deployed images.

Pinned images (7 Dockerfiles):
- golang:1.25-alpine → sha256:c4ea15b... (workspace-server/Dockerfile,
  Dockerfile.dev, Dockerfile.tenant, tests/harness/cp-stub/Dockerfile)
- alpine:3.20 → sha256:c64c687c... (workspace-server/Dockerfile,
  tests/harness/cp-stub/Dockerfile)
- node:20-alpine → sha256:afdf982... (workspace-server/Dockerfile.tenant)
- node:22-alpine → sha256:cb15fca... (canvas/Dockerfile)
- python:3.11-slim → sha256:e78299e... (workspace/Dockerfile)
- nginx:1.27-alpine → sha256:62223d6... (tests/harness/cf-proxy/Dockerfile)

Note: docker-compose.yml service images (postgres, redis, clickhouse,
litellm, ollama) are intentionally left on major-version tags — those
are runtime-pulled and updated regularly for local-dev ergonomics.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 23:56:39 +00:00
Molecule AI Core Platform Lead
aff8831817 Merge remote-tracking branch 'origin/main' into trig-197
All checks were successful
sop-tier-check / tier-check (pull_request) Successful in 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 6s
audit-force-merge / audit (pull_request) Successful in 4s
2026-05-09 23:44:38 +00:00
2da036204c test(canvas): add tests for Cmd/Ctrl+Arrow keyboard node resize
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
Add 10 tests covering the Cmd/Ctrl+Arrow resize shortcut:
- ArrowUp/Down resizes height (−/+10px)
- ArrowLeft/Right resizes width (−/+10px)
- Shift modifier uses 2px step for fine control
- min-height constraint respected when shrinking
- Guard: no-op when no node selected
- Guard: skipped when modal dialog is open
- Plain arrow keys (no modifier) fire moveNode instead
- Alt+Arrow is skipped (not a resize combo)

Also extends the mock store state with `onNodesChange` and node
`width`/`height` fields needed for the resize tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 23:41:29 +00:00
4a73a72e44 test(canvas): add KeyboardShortcutsDialog a11y render tests
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 6s
Cherry-picked from feat/keyboard-shortcuts-dialog-test (99ecdd6d).
6 tests covering role=dialog, aria-modal, aria-labelledby,
no-render-when-closed, Escape-close, focus-on-open, Tab trap.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 23:18:14 +00:00
b837d3b065 fix(canvas): text-ink-soft → text-ink-mid for WCAG AA contrast
Replace all text-ink-soft usages across canvas components and app pages.
ink-soft (#8d92a0) on dark zinc (#0e1014) yields ~2.2:1 contrast,
failing WCAG 2.1 AA minimum of 4.5:1 for normal text.

ink-mid (#c8c2b4) on dark zinc yields ~7.6:1 — well above AA.

text-ink-mid is already the semantic token for secondary/caption text
in the warm-paper light mode; the dark-mode override was the gap.

52 files, 268 replacements. No functional change beyond contrast.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 23:18:14 +00:00
f5682fbb5f docs(canvas): mark keyboard node drag as done in audit
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 23:18:14 +00:00
534cdb5aa4 feat(canvas): keyboard-accessible node resize via Cmd/Ctrl+Arrow
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
sop-tier-check / tier-check (pull_request) Failing after 5s
Cmd/Ctrl+Arrow Up/Down resizes node height (±10px, ±2px with Shift).
Cmd/Ctrl+Arrow Left/Right resizes node width (±10px, ±2px with Shift).
Uses the same onNodesChange('dimensions') path that NodeResizer uses
— no new store action needed. Respects min-width/min-height matching
the NodeResizer constraints (360×200 with children, 210×110 without).

The Arrow-key move shortcut now skips when a modifier key is held,
so Cmd/Ctrl+Arrow unambiguously means resize (not move).

Updates canvas audit doc: Node Rendering section updated and
the LOW node-resize item marked done. All Remaining Gaps items
are now complete.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 23:10:51 +00:00
19bb3430e5 feat(canvas): keyboard-accessible edge anchors via Enter/Space
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 15s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 16s
Target handle (top of card): Enter/Space extracts this node from
its parent, moving it to the root level.

Source handle (bottom of card): Enter/Space nests the currently
selected node as a child of this node (requires another node to be
selected first).

Both handles gain tabIndex=0, role="button", a descriptive aria-label,
and a blue focus ring so keyboard-only users can navigate the
workspace hierarchy without a mouse. Uses the existing nestNode store
action — no new API surface needed.

Updates the canvas audit doc to mark the LOW edge-anchor item done.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 22:52:33 +00:00
3e2ff63f7f feat(canvas): keyboard-accessible node drag via Arrow keys
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
Closes canvas audit item: MEDIUM keyboard-accessible node drag.

- Arrow keys move the selected node by 10px per press; Shift+Arrow
  moves by 50px. Position is persisted to the backend via savePosition.
- The modal-dialog guard (same pattern as ? shortcut) prevents Arrow
  keys from moving nodes when a modal like KeyboardShortcutsDialog is
  open — dialogs own their own arrow semantics.
- All shortcuts guarded by the inInput check so Arrow keys still work
  for text navigation inside inputs/textareas.

Changes:
- canvas.ts: new moveNode(dx, dy) store action — updates position
  directly without the grow-parents pass that onNodesChange runs on
  every drag tick (avoids edge-chase flicker).
- useKeyboardShortcuts.ts: Arrow key handler added.
- canvas.test.ts: new moveNode unit tests (position update, no-op,
  savePosition call).
- useKeyboardShortcuts.test.tsx: new integration tests for all
  keyboard shortcuts including the new Arrow key handlers.
- canvas-audit-items.md: Keyboard Shortcuts section upgraded to ,
  drag item marked done.
- canvas-events.test.ts: fix pre-existing double-}); syntax error.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 22:19:01 +00:00
Molecule AI Core Platform Lead
3525ee61a4 Merge remote-tracking branch 'origin/main' into trig-175
All checks were successful
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
sop-tier-check / tier-check (pull_request) Successful in 5s
audit-force-merge / audit (pull_request) Successful in 4s
2026-05-09 21:57:20 +00:00
Molecule AI Core Platform Lead
35945d26da Merge remote-tracking branch 'origin/main' into trig-175 2026-05-09 21:55:14 +00:00
Molecule AI Core Platform Lead
d72bef93bc Merge remote-tracking branch 'origin/main' into trig-171
All checks were successful
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
sop-tier-check / tier-check (pull_request) Successful in 4s
audit-force-merge / audit (pull_request) Successful in 4s
2026-05-09 21:50:37 +00:00
Molecule AI Core Platform Lead
ca74a9c064 Merge remote-tracking branch 'origin/main' into trig-171 2026-05-09 21:48:43 +00:00
b33f1feb79 feat(canvas): add keyboard shortcuts help dialog + global ? trigger
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
Closes the "no keyboard shortcut help dialog" audit gap (MEDIUM).

Changes:
- Add KeyboardShortcutsDialog component: portal-based, accessible
  dialog listing all canvas + navigation + agent shortcuts grouped by
  category. WCAG 2.1 compliant (focus trap, Esc close, aria-modal,
  aria-labelledby, focus restoration on close).
- Add global ? shortcut: opens the dialog when pressed outside any
  input field and no modal is already open.
- Add "See all shortcuts →" link in the Toolbar quick-start popup
  linking to the dialog.

Test plan:
- [x] npx vitest run (182 tests pass)
- [x] tsc --noEmit (no type errors)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 21:47:34 +00:00
1224f19cfc feat(canvas): screen reader live announcements for canvas state changes
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
sop-tier-check / tier-check (pull_request) Failing after 4s
Issue: HIGH priority item from canvas accessibility audit (2026-05-09).
Screen reader users had no way to know when workspace status changed
— the canvas updated visually but no announcement was made.

Changes:
- canvas.ts: add `liveAnnouncement: string` + `setLiveAnnouncement` to
  CanvasState so the store can hold the current announcement text.
- canvas-events.ts: set `liveAnnouncement` in handleCanvasEvent for 6
  key status transitions: ONLINE, OFFLINE, PAUSED, DEGRADED, PROVISIONING,
  REMOVED, PROVISION_FAILED. Names are looked up from store nodes so
  announcements are human-readable ("Alpha is now online" not "ws-1").
  TASK_UPDATED and AGENT_MESSAGE are intentionally excluded — they fire
  on every heartbeat and would overwhelm the user.
- Canvas.tsx: subscribe to `liveAnnouncement` from the store; render a
  visually-hidden `aria-live="polite" aria-atomic="true"` region that
  speaks the announcement then clears it after 500 ms so the same
  message doesn't re-announce on re-render. Fallback still announces
  workspace count on initial load.
- canvas-events.test.ts: 12 new test cases covering announcement
  content for all 6 event types, empty/no-announcement cases, and
  payload-name fallback when a node isn't yet in the store.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 21:30:33 +00:00
d15040d233 fix(canvas): render delegation responses as normal messages not error banners
Some checks failed
sop-tier-check / tier-check (pull_request) Failing after 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
Issue #159: successful delegation responses were rendered as error
banners because extractResponseText() only handled the A2A result
format (body.result.parts[].text) but delegation.go stores
response_body as {text: "...", delegation_id: "..."}. The error
status was set when the HTTP transport failed even though the actual
agent response was received.

Fixes:
1. extractResponseText: check body.text before the result path so
   delegation response_body.text is extracted correctly
2. extractResponseText: also check body.response_preview (WS event shape
   from DELEGATION_COMPLETE handler)
3. GroupedCommsView: render NormalMessage when status=error but
   responseText is populated (delegation succeeded, transport failed)
   instead of burying the content in an error banner

Tests: 8 new cases (4 extractResponseText + 2 extractRequestText
regression + 2 render tests). 189 tests pass across 10 files.

Closes #159.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 21:26:39 +00:00