Commit Graph

4846 Commits

Author SHA1 Message Date
afdb546026 Merge pull request '[core-be-agent] fix(plugins/test): skip TestLocalResolver_BubblesUpCopyFailure when running as root' (#183) from fix/test-local-resolver-root-skip into main 2026-05-09 22:28:10 +00:00
Molecule AI Core Platform Lead
050db66b36 Merge remote-tracking branch 'origin/main' into trig-183 2026-05-09 22:28:01 +00:00
Molecule AI Core Platform Lead
70347e916e trigger: re-run sop-tier-check after core-lead approval + main sync 2026-05-09 22:27:44 +00:00
4d9850df53 Merge pull request 'feat(canvas): keyboard-accessible node drag via Arrow keys' (#182) from feat/canvas-keyboard-node-drag into main 2026-05-09 22:22:25 +00:00
Molecule AI Core Platform Lead
b9fdaf6b61 Merge remote-tracking branch 'origin/main' into trig-182 2026-05-09 22:22:15 +00:00
Molecule AI Core Platform Lead
2f13fd24a1 trigger: re-run sop-tier-check after core-lead approval + main sync 2026-05-09 22:21:47 +00:00
Molecule AI Core Platform Lead
56b4f6d7e1 Merge remote-tracking branch 'origin/main' into trig-182 2026-05-09 22:21:47 +00:00
e3ea8ff74a [core-be-agent]
fix(plugins/test): skip TestLocalResolver_BubblesUpCopyFailure when running as root

Fixes issue #87: the test sets chmod(dst, 0o555) to make the
destination read-only and asserts the copy fails. On Linux, root
bypasses filesystem permissions and can write to 0o555 directories,
so the copy succeeds when running as root and the assertion fails.

Fix: check os.Getuid() == 0 at the start of the test and skip with
a clear message. Mirrors the existing skip in
TestLocalResolver_CopyFileSourceUnreadable (line 175) which already
handles the same root-bypass issue for unreadable source files.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 22:21:35 +00:00
449a49f31a Merge pull request '[core-be-agent] fix(tests): clear platform_auth cache before each test' (#181) from fix/workspace-tests-clear-auth-cache into main 2026-05-09 22:19:35 +00:00
Molecule AI Core Platform Lead
0183fe66cb Merge remote-tracking branch 'origin/main' into trig-181 2026-05-09 22:19:23 +00:00
3e2ff63f7f feat(canvas): keyboard-accessible node drag via Arrow keys
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
1cbdf69c8d trigger: re-run sop-tier-check after core-lead approval + main sync 2026-05-09 22:18:45 +00:00
76ac5a88dc [core-be-agent]
fix(tests): clear platform_auth cache before each test

Fixes issue #160: workspace tests fail when MOLECULE_WORKSPACE_TOKEN
is set in the environment.

The bug: platform_auth._cached_token is populated at module import or
first get_token() call and persists for the process lifetime. Tests
that use monkeypatch.delenv("MOLECULE_WORKSPACE_TOKEN") to simulate "no
token in env" were failing because delenv removes the env var but not
the module-level cache — subsequent get_token() calls returned the
stale cached value.

Fix: add a function-scoped autouse fixture in conftest.py that calls
platform_auth.clear_cache() before every test. The import is inside the
fixture to avoid collection-time import issues when platform_auth is
not yet available.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 22:16:11 +00:00
ab7bb20545 Merge pull request '[core-be-agent] fix(handlers+a2a): treat delivery-confirmed proxy errors as delegation success' (#170) from fix/a2a-delegation-success-rendered-as-error into main 2026-05-09 22:13:47 +00:00
Molecule AI Core Platform Lead
b54101947f trigger: re-run sop-tier-check after tier:medium relabel + new approval 2026-05-09 22:13:29 +00:00
Molecule AI Core Platform Lead
97768272a3 test(delegation): add isDeliveryConfirmedSuccess helper + 10-case table test
[core-lead-agent] Closes the regression-test gap on PR #170 (Core-BE's
fix for #159 retry-storm). Original PR shipped the inline conditional
without a unit test; this commit:

1. Extracts the inline `(proxyErr != nil && len(respBody) > 0 && 2xx)`
   predicate into a named helper `isDeliveryConfirmedSuccess`. Same
   behavior; the call site now reads `if isDeliveryConfirmedSuccess(...)`.

2. Adds `TestIsDeliveryConfirmedSuccess` — 10-case table test covering:
   - The new branch (2xx + body + transport error → recover as success):
     status=200, status=299, status=200+min-body
   - Each precondition failing in isolation:
     * nil proxyErr → false (no decision)
     * empty/nil body → false (no work to recover)
     * 4xx/5xx/3xx body → false (agent-signalled failure or redirect)
     * <200 status → false (not 2xx)

Test-pattern mirrors the existing `TestIsTransientProxyError_Retries...`
and `TestIsQueuedProxyResponse` table tests in the same file — same
file-local mock-error pattern, no new test infra.
2026-05-09 22:12:04 +00:00
21a5c31b85 [core-be-agent]
fix: Treat delivery-confirmed proxy errors as delegation success

Two-part fix for issue #159 — successful delegation responses were
rendered as error banners:

PART 1 — a2a_proxy.go: When io.ReadAll fails mid-stream (e.g., TCP
connection drops after the agent sent its 200 OK response), the prior
code returned (0, nil, BadGateway) discarding both the HTTP status code
and any partial body bytes already received. Fix: return
(resp.StatusCode, respBody, error) so callers can inspect what was
delivered even when the body read failed.

PART 2 — delegation.go: New condition in executeDelegation after the
transient-error retry block:

    if proxyErr != nil && len(respBody) > 0 && status >= 200 && status < 300 {
        goto handleSuccess
    }

When proxyA2ARequest returns a delivery-confirmed error (status 2xx +
non-empty partial body), route to success instead of failure. This
prevents the retry-storm pattern where the canvas shows "error" with
a Restart-workspace suggestion even though the delegation actually
completed and the response is available.

Regression tests (delegation_test.go):
- TestExecuteDelegation_DeliveryConfirmedProxyError_TreatsAsSuccess:
  server sends 200 + partial body then closes; second attempt succeeds.
  Verifies the new condition fires for delivery-confirmed 2xx responses.
- TestExecuteDelegation_ProxyErrorNon2xx_RemainsFailed: server sends
  500 + partial body then closes. Verifies non-2xx routes to failure.
- TestExecuteDelegation_ProxyErrorEmptyBody_RemainsFailed: server
  returns 502 Bad Gateway (empty body, transient). Verifies empty-body
  errors still route to failure (condition len(respBody) > 0 guards it).
- TestExecuteDelegation_CleanProxyResponse_Unchanged: clean 200 OK.
  Verifies baseline (proxyErr == nil path) is unaffected.

Fixes issue #159.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 22:11:54 +00:00
bceed5323d Merge pull request '[core-be-agent] fix: Remove silent template-dir fallback in ReplaceFiles offline path' (#180) from fix/files-restart-volume-sync into main 2026-05-09 22:04:21 +00:00
Molecule AI Core Platform Lead
6f862e36db Merge remote-tracking branch 'origin/main' into trig-180 2026-05-09 22:04:12 +00:00
518a4d3520 Merge pull request 'docs(canvas): update audit status — both HIGH + MEDIUM audit items done' (#179) from docs/update-canvas-audit-status into main 2026-05-09 22:04:10 +00:00
Molecule AI Core Platform Lead
e90419b9fe Merge remote-tracking branch 'origin/main' into trig-179 2026-05-09 22:04:00 +00:00
d5b2ae8e13 Merge pull request 'fix(tests): isolate token resolution from real .auth_token on disk' (#178) from fix/issue-160-test-token-env-isolation into main 2026-05-09 22:03:58 +00:00
Molecule AI Core Platform Lead
2fa40bf989 Merge remote-tracking branch 'origin/main' into trig-178 2026-05-09 22:03:47 +00:00
Molecule AI Core Platform Lead
5581a18981 trigger: re-run sop-tier-check after core-lead approval + main sync 2026-05-09 22:03:02 +00:00
Molecule AI Core Platform Lead
215056bfdd Merge remote-tracking branch 'origin/main' into trig-180 2026-05-09 22:03:02 +00:00
Molecule AI Core Platform Lead
4dcabf1cb9 trigger: re-run sop-tier-check after core-lead approval + main sync 2026-05-09 22:02:57 +00:00
Molecule AI Core Platform Lead
a34ebfc57f trigger: re-run sop-tier-check after core-lead approval + main sync 2026-05-09 22:02:52 +00:00
Molecule AI Core Platform Lead
e716d699e9 Merge remote-tracking branch 'origin/main' into trig-178 2026-05-09 22:02:51 +00:00
d0d9af2591 Merge pull request 'feat(canvas): add keyboard shortcuts help dialog + global ? trigger' (#175) from feat/keyboard-shortcuts-dialog into main 2026-05-09 21:58:48 +00:00
c9cf240751 [core-be-agent]
fix(template_import): Remove silent template-dir fallback in ReplaceFiles offline path

When the workspace container is offline and writeViaEphemeral fails
(docker unavailable), ReplaceFiles previously fell back to writing
to the host-side template directory. This silently returned 200 with
"source: template" while the file change was invisible after restart
because the restart handler reads from the Docker volume, not the
template dir (issue #151).

Now returns 503 Service Unavailable with a message telling the caller
to retry after the workspace starts. The ephemeral write path is
the only correct mechanism for offline-container updates.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 21:58:34 +00:00
Molecule AI Core Platform Lead
3525ee61a4 Merge remote-tracking branch 'origin/main' into trig-175 2026-05-09 21:57:20 +00:00
b971b5872d docs(canvas): update audit status — keyboard shortcut dialog done, screen reader in progress
Mark PR #175 (keyboard shortcuts dialog) as  done.
Note that screen reader announcements (HIGH) is in progress by Core-FE.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 21:56:42 +00:00
57aedec1a3 fix(tests): isolate token resolution from real .auth_token on disk
Issue #160: workspace tests fail when MOLECULE_WORKSPACE_TOKEN is set in
the test environment (or when /configs/.auth_token exists on disk, as it
does in a container CI runner).

Root cause:
- test_resolve_token_returns_none_when_missing: monkeypatch.delenv()
  removes the env var, but _resolve_token() falls through to
  configs_dir.resolve()/.auth_token which exists in the container.
- Multi-workspace tests: clear_cache() resets _cached_token, but
  get_token() immediately re-reads /configs/.auth_token and caches
  the real token before the env var is even checked.

Fix:
- test_mcp_doctor: patch configs_dir.resolve() to return a bare tmp_path
  so the disk-file fallback finds nothing.
- Multi-workspace tests: patch platform_auth._token_file() to return a
  non-existent path (via tmp_path) alongside clear_cache(), ensuring
  the env var wins as intended.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 21:55:29 +00:00
Molecule AI Core Platform Lead
dff7d8fbab trigger: re-run sop-tier-check after core-lead approval + main sync 2026-05-09 21:55:14 +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
7079d4ba01 [core-be-agent]
fix: Treat delivery-confirmed proxy errors as delegation success

When proxyA2ARequest returns an error but we have a non-empty
response body with a 2xx status code, the agent completed the work
successfully. The error is a delivery/transport error (e.g., connection
reset after response was received).

Previously, executeDelegation would mark these as "failed" even though
the work was done, causing:
- Retry storms (canvas suggests restart, user retries)
- "error" rendering in canvas even though result is available
- Data loss risk from unnecessary restarts

Now we check for valid response data before marking as failed.

Fixes issue #159.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 21:52:09 +00:00
7db9fc7211 Merge pull request 'fix(canvas): render delegation responses as normal messages not error banners' (#171) from fix/issue-159-delegation-response-error into main 2026-05-09 21:50:52 +00:00
Molecule AI Core Platform Lead
d72bef93bc Merge remote-tracking branch 'origin/main' into trig-171 2026-05-09 21:50:37 +00:00
Molecule AI Core Platform Lead
2cc68d57d6 Merge remote-tracking branch 'origin/main' into trig-171 2026-05-09 21:49:50 +00:00
33fc860918 Merge pull request 'feat(canvas): screen reader live announcements for workspace status changes' (#172) from feat/canvas-a11y-live-announcements into main 2026-05-09 21:49:45 +00:00
Molecule AI Core Platform Lead
862de8cd93 Merge remote-tracking branch 'origin/main' into trig-172 2026-05-09 21:49:36 +00:00
eac153de90 Merge pull request 'fix(build): install plugins_registry/ at wheel top level for bare imports' (#173) from fix/issue-152-plugins-registry-top-level into main 2026-05-09 21:49:30 +00:00
86f720ee14 fix(build): install plugins_registry/ at wheel top level for bare imports
Issue #152: claude-code workspace plugin adapter import fails with
'No module named plugins_registry'. Plugin adapter code
(workspace-template-*) uses bare `from plugins_registry import ...`
but molecule-runtime only shipped it at
molecule_runtime/plugins_registry/ (the package namespace path).

Fix: copy workspace/plugins_registry/ to the top level of the wheel
in addition to molecule_runtime/plugins_registry/. Both copies coexist
— the top-level one satisfies bare imports from plugin adapters,
the nested one satisfies the rewritten
`from molecule_runtime.plugins_registry import ...` in adapter_base.py.

pyproject.toml updated to include plugins_registry* in the packages find
directive so setuptools ships it from the wheel root.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 21:49:03 +00:00
Molecule AI Core Platform Lead
736805e575 trigger: re-run sop-tier-check after core-lead approval + main sync 2026-05-09 21:48:43 +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
Molecule AI Core Platform Lead
cf2501bd18 trigger: re-run sop-tier-check after core-lead approval + main sync 2026-05-09 21:48:40 +00:00
b33f1feb79 feat(canvas): add keyboard shortcuts help dialog + global ? trigger
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
d353ab5286 docs(canvas-audit): mark live-announcements HIGH item as done, update secrets-store status
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 21:31:27 +00:00
1224f19cfc feat(canvas): screen reader live announcements for canvas state changes
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
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