role="alert" is for passive announcements. A delete confirmation with
Confirm/Cancel action buttons requires a user response, which is the
semantics of role="alertdialog" (interactive dialog requiring response).
- Replace role="alert" with role="alertdialog" + aria-modal="true"
- Add aria-labelledby="delete-confirm-title" for an accessible name
- Add <h3 id="delete-confirm-title"> as the labelling element
("Confirm deletion") so AT announces the dialog purpose on focus
Closes#905
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Memory keys can contain characters like [ ] / : . # and spaces that make
invalid HTML id values (breaks CSS selectors and ARIA id-ref lookups).
- Add sanitizeId() helper: replaces non-alphanumeric chars with hyphens,
collapses consecutive hyphens, strips leading/trailing hyphens
- Compute bodyId = "mem-body-{sanitizeId(entry.key)}" in MemoryEntryRow
- Set id={bodyId} on the expanded body container
- Set aria-controls={bodyId} on the toggle button so AT can navigate
directly between the button and its controlled panel
Closes#904
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Memory keys can contain characters like [ ] / : . # and spaces that make
invalid HTML id values (breaks CSS selectors and ARIA id-ref lookups).
- Add sanitizeId() helper: replaces non-alphanumeric chars with hyphens,
collapses consecutive hyphens, strips leading/trailing hyphens
- Compute bodyId = "mem-body-{sanitizeId(entry.key)}" in MemoryEntryRow
- Set id={bodyId} on the expanded body container
- Set aria-controls={bodyId} on the toggle button so AT can navigate
directly between the button and its controlled panel
Closes#904
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add aria-pressed={filter === f.id} to every filter pill button so AT
announces which filter is currently active
- Add aria-pressed={autoRefresh} to the auto-refresh toggle so AT
announces the live/paused state when the button is activated
Closes#903
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add aria-pressed={filter === f.id} to every filter pill button so AT
announces which filter is currently active
- Add aria-pressed={autoRefresh} to the auto-refresh toggle so AT
announces the live/paused state when the button is activated
Closes#903
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add role="alert" to the global error banner and the inline add-form
error message so screen readers announce errors immediately on render
- Add aria-label to all three add-form inputs (key / value / TTL) so
every form control has an accessible name (was flagged as unlabelled)
- Add aria-expanded={expanded === entry.key} to each entry toggle button
so AT announces collapsed/expanded state on activation
Closes#902
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add role="alert" to the global error banner and the inline add-form
error message so screen readers announce errors immediately on render
- Add aria-label to all three add-form inputs (key / value / TTL) so
every form control has an accessible name (was flagged as unlabelled)
- Add aria-expanded={expanded === entry.key} to each entry toggle button
so AT announces collapsed/expanded state on activation
Closes#902
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The error banner div introduced in the MemoryInspectorPanel (PR #892)
was missing role="alert", regressing the a11y standard established in
PR #877 / issue #830. Screen readers now announce the error immediately
on render.
Closes#901
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The error banner div introduced in the MemoryInspectorPanel (PR #892)
was missing role="alert", regressing the a11y standard established in
PR #877 / issue #830. Screen readers now announce the error immediately
on render.
Closes#901
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The replace directive `=> /plugin` breaks CI builds where go build runs
natively (no /plugin directory). Move the replace to Dockerfile RUN so
it only applies during Docker builds where the plugin is COPYed.
Fixes: "replacement directory /plugin does not exist" on CI runner.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The replace directive `=> /plugin` breaks CI builds where go build runs
natively (no /plugin directory). Move the replace to Dockerfile RUN so
it only applies during Docker builds where the plugin is COPYed.
Fixes: "replacement directory /plugin does not exist" on CI runner.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Windows Docker Desktop copies host files with CRLF even when
.gitattributes says eol=lf. The entrypoint now strips \r from all
hook .sh/.py files before dropping to agent user. Permanent fix for
the #507 CRLF regression that reappeared after every restart.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Windows Docker Desktop copies host files with CRLF even when
.gitattributes says eol=lf. The entrypoint now strips \r from all
hook .sh/.py files before dropping to agent user. Permanent fix for
the #507 CRLF regression that reappeared after every restart.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds callback_query to AllowedUpdates in Telegram polling. When CEO
clicks Yes/No inline keyboard buttons:
1. Acknowledges press (removes loading spinner)
2. Updates message with 'CEO approved/rejected'
3. Routes 'CEO_DECISION: approve:xyz' as inbound to the agent
Only one workspace polls per bot token (Triage Operator) — other
workspaces with Telegram use outbound-only via direct API.
Fixed: duplicate pollers causing 'terminated by other getUpdates'
errors — removed PM/DevLead/ResearchLead Telegram channel rows
(they send outbound via direct Telegram API calls, not channel manager).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds callback_query to AllowedUpdates in Telegram polling. When CEO
clicks Yes/No inline keyboard buttons:
1. Acknowledges press (removes loading spinner)
2. Updates message with 'CEO approved/rejected'
3. Routes 'CEO_DECISION: approve:xyz' as inbound to the agent
Only one workspace polls per bot token (Triage Operator) — other
workspaces with Telegram use outbound-only via direct API.
Fixed: duplicate pollers causing 'terminated by other getUpdates'
errors — removed PM/DevLead/ResearchLead Telegram channel rows
(they send outbound via direct Telegram API calls, not channel manager).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
HermesA2AExecutor now supports sending system context as ordered, separate
role=system messages instead of a single concatenated string — the model
format recommended by NousResearch.
Changes:
- HermesA2AExecutor.__init__: new system_blocks kwarg (list[str|None]|None)
stored as an independent copy; None blocks and empty strings silently skipped
- _build_messages(): when system_blocks is not None, emits each non-empty
block as a separate {"role": "system"} entry in Hermes-recommended order
(persona → tools context → reasoning policy); falls through to legacy
system_prompt path when system_blocks is None (backward compatible)
Backward compatibility: existing callers that pass a single system_prompt
string continue to work identically — no changes required.
Tests (12 new, 47 total):
- system_blocks stored as independent copy (mutation safe)
- three-block stacked ordering preserved
- empty / None blocks silently skipped
- all-empty list → zero system messages
- system_blocks overrides system_prompt when both provided
- legacy system_prompt path unchanged
- stacked blocks appear in the live API call kwargs
Closes#499
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
HermesA2AExecutor now supports sending system context as ordered, separate
role=system messages instead of a single concatenated string — the model
format recommended by NousResearch.
Changes:
- HermesA2AExecutor.__init__: new system_blocks kwarg (list[str|None]|None)
stored as an independent copy; None blocks and empty strings silently skipped
- _build_messages(): when system_blocks is not None, emits each non-empty
block as a separate {"role": "system"} entry in Hermes-recommended order
(persona → tools context → reasoning policy); falls through to legacy
system_prompt path when system_blocks is None (backward compatible)
Backward compatibility: existing callers that pass a single system_prompt
string continue to work identically — no changes required.
Tests (12 new, 47 total):
- system_blocks stored as independent copy (mutation safe)
- three-block stacked ordering preserved
- empty / None blocks silently skipped
- all-empty list → zero system messages
- system_blocks overrides system_prompt when both provided
- legacy system_prompt path unchanged
- stacked blocks appear in the live API call kwargs
Closes#499
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Dockerfile COPY for molecule-ai-plugin-github-app-auth was lost
during a rebase earlier this session. Without it, the platform binary
compiled without the TokenProvider interface implementation, causing
/admin/github-installation-token to return 'no token provider registered'.
This forced hourly rolling restarts to refresh GH_TOKEN (the env var
from provision time expires after ~60 min). Each restart also required
re-applying 6 manual patches and caused ~2 min of A2A downtime where
agents reported peers as 'unresponsive'.
With this fix, the gh-wrapper in each container auto-refreshes tokens
via the platform endpoint on every gh call. Zero restarts needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Dockerfile COPY for molecule-ai-plugin-github-app-auth was lost
during a rebase earlier this session. Without it, the platform binary
compiled without the TokenProvider interface implementation, causing
/admin/github-installation-token to return 'no token provider registered'.
This forced hourly rolling restarts to refresh GH_TOKEN (the env var
from provision time expires after ~60 min). Each restart also required
re-applying 6 manual patches and caused ~2 min of A2A downtime where
agents reported peers as 'unresponsive'.
With this fix, the gh-wrapper in each container auto-refreshes tokens
via the platform endpoint on every gh call. Zero restarts needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a concise action advisory for engineering leads summarising the 9 open
findings from the full SAFE-MCP audit, with immediate remediation steps for
NEW-003 (unpinned npm packages in .mcp.json — HIGH), a Phase 35 scoping
recommendation for plugin supply-chain hardening (VULN-003, VULN-004), and
medium-term GLOBAL memory scope controls (VULN-002, VULN-005).
Pairs with: monorepo PR #808, docs PR #18
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a concise action advisory for engineering leads summarising the 9 open
findings from the full SAFE-MCP audit, with immediate remediation steps for
NEW-003 (unpinned npm packages in .mcp.json — HIGH), a Phase 35 scoping
recommendation for plugin supply-chain hardening (VULN-003, VULN-004), and
medium-term GLOBAL memory scope controls (VULN-002, VULN-005).
Pairs with: monorepo PR #808, docs PR #18
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Per UIUX Cycle 5 spec, Dialog.Content should carry an explicit
aria-label="Conversation trace" in addition to the aria-labelledby
automatically wired by Radix Dialog via Dialog.Title. This provides
a fallback accessible name directly on the dialog container element.
All 732 tests pass, build clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Per UIUX Cycle 5 spec, Dialog.Content should carry an explicit
aria-label="Conversation trace" in addition to the aria-labelledby
automatically wired by Radix Dialog via Dialog.Title. This provides
a fallback accessible name directly on the dialog container element.
All 732 tests pass, build clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
molecule-medo now lives at Molecule-AI/molecule-ai-plugin-molecule-medo
(same pattern as all other plugins). Removed the gitignore exception
that kept it in the monorepo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
molecule-medo now lives at Molecule-AI/molecule-ai-plugin-molecule-medo
(same pattern as all other plugins). Removed the gitignore exception
that kept it in the monorepo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>