Skip to content

feat(skills-editor): skills editor with MCP governance and accessibility hardening#397

Closed
anfibiacreativa wants to merge 22 commits into
ewfrom
feat/ew-skills-editor-recovered
Closed

feat(skills-editor): skills editor with MCP governance and accessibility hardening#397
anfibiacreativa wants to merge 22 commits into
ewfrom
feat/ew-skills-editor-recovered

Conversation

@anfibiacreativa
Copy link
Copy Markdown
Member

@anfibiacreativa anfibiacreativa commented Apr 28, 2026

Summary

  • Introduce the standalone nx-skills-editor experience with a master-detail layout for Skills, Prompts, Agents, MCPs, and Memory.
  • Add MCP governance UX (tool listing + granular enable/disable) and wire chat to use site-configured MCP servers and headers.
  • Harden accessibility and keyboard behavior for card-based interactions, plus targeted regression and unit test coverage.

Why

  • The prior skills/editor surfaces were fragmented and hard to validate end-to-end with chat and MCP flows.
  • Authors need a single place to manage capabilities, including private MCP servers and per-tool governance.
  • Recent interaction bugs (nested-control activation, prompt actions, slash/keyboard behavior, stale form state) required explicit guardrails and tests.

What Changed

  • Add new building blocks for the editor shell:
    • nx2/blocks/shared/card/* reusable card component.
    • nx2/blocks/shared/tabs/* reusable tabs component.
    • nx2/blocks/skills-editor/* new Skills Editor block + API module.
  • Implement full master-detail Skills Editor workflows:
    • Skills CRUD and sync behavior.
    • Prompts catalog/editor fixes (visible actions, delete path, add-to-chat parity).
    • Agents catalog/presets and form handling.
    • MCP catalog/editor, custom server registration, built-in tool visibility, and edit-state fixes.
    • Memory panel rendering and layout handling.
    • Improve skills-editor load performance by caching repeated config reads and moving orphan skill synchronization to a non-blocking background step.
  • Add MCP + chat integration:
    • Chat loads configured MCP server URLs and server headers from config.
    • Chat controller includes mcpServers and mcpServerHeaders in agent requests.
    • Skills editor supports MCP auth header entry and persistence fields in config.
  • Add tool-governance plumbing in UI:
    • Load/display server tools.
    • Persist per-tool enable/disable overrides.
    • Harden boolean parsing from config sheets for consistent enabled-state behavior.
  • Accessibility and interaction hardening:
    • Keyboard-activatable catalog rows/cards with explicit semantics.
    • Guard parent activation when nested interactive controls are used.
    • Focus-visible treatment for card-like interactive surfaces.
  • Supporting updates and regressions:
    • Icon asset additions and nav/icon loading updates.
    • Prompt/chat event compatibility during migration (da-skills-lab-* and da-skills-editor-* namespaces).
    • Remove unreachable Generated Tools tab from catalog.
    • Stabilize flaky tree throttling test threshold in test/utils/tree.test.js.
    • Add/expand unit coverage in nx2/test/unit/skills-editor/nx-skills-editor.test.js.

Test Plan

  • Unit: npm run test:unit -- nx2/test/unit/skills-editor/nx-skills-editor.test.js
  • Unit: run broader da-nx unit suite to verify shared card/tabs integration.
  • E2E: run Skills Lab Playwright coverage on tests/playwright-e2e branch (keyboard/a11y and regression paths).

e2e test live here -> https://github.com/adobe/da-live/compare/tests/playwright-e2e?expand=1

Manual:

  • Validate MCP custom/built-in card interactions (click + keyboard).
  • Validate prompt add-to-chat and slash-command listing/filter/navigation behavior.
  • Validate MCP server auth headers are saved and threaded into chat requests.

Risks / Follow-ups

  • Playwright non-auth runs can fail early if nx-skills-editor does not mount; add clearer readiness skip messaging if needed. These tests are in da-live. Non-blocking to merge PR
  • MCP card interaction architecture should continue trending toward non-interactive containers plus explicit controls to reduce semantic regressions.
  • Minor naming/cleanup follow-ups may remain in skills-editor API/state fields and can be handled in a cleanup PR.

Must be integrated together with:
adobe-rnd/da-agent#23

Stage URLs:

https://da.live/apps/skills?nx=feat-ew-skills-editor-recovered&nxver=2#/exp-workspace/frescopa
https://da.live/canvas?nx=feat-ew-skills-editor-recovered&nxver=2#/exp-workspace/frescopa/index

ARIA tablist with keyboard nav, S2 tokens, tab-change event.

Made-with: Cursor
Heading, subheading, badge and actions slots, S2 tokens.

Made-with: Cursor
…ation

Port skills lab from exp-workspace to nx2 with S2 tokens, semantic
ARIA markup, and non-blocking data loading. Wire into canvas after-panel.

Made-with: Cursor
- rename "handoff" → "suggestion" throughout (state, CSS, events, sessionStorage key)
- fix SVG node sharing: store outerHTML string, render via unsafeHTML so each card gets its own node
- fix timer leak: store _statusTimer, clear on disconnect and on re-call
- add error handling to _onDeleteSkill and _onDeletePrompt
- remove dead dynamic import of registerMcpServer (already statically imported)
- declare _loadedKey and _statusTimer in constructor (no more implicit properties)
- extract STATUS constants, replace all 'approved'/'draft' magic strings
- fix textarea flex-fill: remove calc(100vh) hack, propagate flex through .textarea-wrap
- remove redundant margin-top on .tool-row (parent gap already provides spacing)
- use S2 checkmark SVG via loadHrefSvg for approved status icon (CSS circles)

Made-with: Cursor
… sync

- Full MCP editor with built-in servers, enable/disable toggles, edit mode
- Generated tools panel with web worker sandbox execution and Try It panel
- Dual-write skills to both .md file and config sheet on save/delete
- Sync mechanism merges .md body with config sheet metadata on load
- Replace loadHrefSvg/unsafeHTML with <img> tags for checkmark icons
Convert Skills Lab from a three-panel to a master-detail layout — slide-in
flex drawer, per-tab dirty-form tracking, browser history + sessionStorage
persistence, tools selector checkbox fix, and all lint errors resolved.
…tab removed

Prompts
- Show row action buttons always (remove opacity:0 hover reveal)
- Add Delete button to each row; remove Associated Tools from editor form
MCPs
- Clicking a card row now opens the editor (was a no-op for custom MCPs)
- Editor title shows 'Edit: <key>' vs 'Register MCP Server' — inline
  'Editing XYZ' hint removed; _clearForm() now also resets MCP state
  so the label can't linger when switching tabs or clicking Register
- Add Description field (textarea-sm) saved to config sheet
Skills editor regression
- _clearForm() was not clearing _editingMcpKey/_mcpKey/_mcpUrl; after
  visiting MCPs, switching to Skills and clicking a skill silently failed
  to open the editor — now fixed
Generated Tools tab
- Remove 'generated' from CATALOG_TABS; tab is no longer reachable
Memory
- Editor body gets .editor-body-memory class → justify-content:flex-end
  pushes memory cont the bottom of the viewport
…d code

API layer:
- deleteSkillMdFile now returns { ok, status } instead of void; callers
  can detect failure (404 treated as success)
- loadAgentPresets spreads preset fields flat ({ id, ...preset }) so
  _renderAgentCard reads label/description/tools without a .preset hop
- loadAgentPresets and fetchMcpToolsFromAgent fire-and-forget chains
  now have .catch() to prevent unhandled rejection warnings

Naming conventions (boolean Lit reactive properties):
- _loading → _isLoading
- _suggestion → _hasSuggestion
- _editorOpen → _isEditorOpen
- _formDirty → _isFormDirty
- _saveBusy → _isSaveBusy
- _agentViewTools → _isAgentViewTools
- _formIsEdit → _isFormEdit
- _formPromptIsEdit → _isFormPromptEdit

CSS state modifier classprefix:
- drawer-open → is-drawer-open
- suggestion-wrap → is-suggestion
- dot-active → is-dot-active
- dot-inactive → is-dot-inactive

Correctness:
- Skill ID input now calls _markDirty() on change
- _loadMemory uses null for fetch error vs '' for empty file; renderer
  handles the distinction correctly
- inert attribute added to .col-editor when drawer is closed
- _onToggleMcpEnabled redundant APPROVED double-check removed
- Dead tab === 'generated' branch removed from _renderListCol()

Polish:
- .card-menu-delete removed; uses .card-menu specificity
- Single-letter vars renamed: q→searchQuery/toolFilter, s→server,
  t→tool, f→status
- 12px font sizes replaced with var(--s2-body-size-xs) token
- Badge padding 2px 8px replaced with spacing tokens
…I contract

Data integrity:
- Sequence skill writes (file first, then config) so a failing config
  write never leaves an .md file without a config entry and vice versa
- Sequence skill deletes (file first, then config) for the same reason
- Prompt CRUD callers updated to use the new ok-shape consistently

API contract (fetchDaConfigSheets / all mutators):
- fetchDaConfigSheets now returns ok:false on 401 instead of EMPTY_CONFIG
  (ok:true); callers can no longer mistake an auth failure for empty data
- All exported mutators (upsertSkillInConfig, deleteSkillFromConfig,
  upsertPromptInConfig, deletePromptFromConfig, upsertGeneratedTool,
  deleteGeneratedTool) now return { ok, error?, status? } consistently
- AUTH_FAIL sentinel extracted to avoid duplicating the 401 return shape
- boot.finally() now has .catch() to prevent unhandled rejection risk
- fetchDaConfigSheets bare catch now surfaces err.message in the return
- Single-letter vars renamed: o→payload, k/u→serverKey/serverUrl, def→toolDef

Stale async guards:
- _onEditSkill: capture skillId and tab at request time; discard response
  if user navigated away before the readSkillMdFile fetch resolved
- _reload fire-and-forgets: loadAgentPresets and fetchMcpToolsFromAgent
  now check the load key before writing state, preventing late responses
  from a previous org/site from overwriting the current view

Form dirty tracking:
- Tool checkbox changes in _renderAssociatedToolsSelector now call
  _markDirty() so tool selection edits are tracked and persisted

Focus management (keyboard accessibility):
- Opening the drawer moves focus to the first interactive element inside
- Closing the drawer restores focus to the element that triggered the open
- _editorTrigger stored on each open entry point (openEditor, onEditSkill,
  openNewMcpEdetc.) and cleared after focus restoration

Filter state:
- _catalogFilter reset to 'all' on every tab change and back/forward
  navigation; it was previously leaking across unrelated tabs

Misc:
- _renderAgentCard parameter renamed a→agent; tool list item t→tool
- _renderAgentsCatalog map var a→agent
…state, CSS deduplication

Critical fixes:
- Add compensating rollback to _onSaveSkill (delete orphan file if config save fails)
  and _onDeleteSkill/_onDeleteSkillById (re-create file if config delete fails)
- Fix writeSkillMdFile to return { ok: false, error } on validation failure
- Replace raw _editorTrigger element reference with stable CSS selector (_editorTriggerSelector)

Simplification:
- Extract upsertSheetRow/deleteSheetRow generic helpers in skills-editor-api.js; all
  config CRUD mutators now delegate to them, eliminating copy-pasted load/mutate/save flows
- Add FRESH_FORM_STATE constant; _clearForm, _captureForm, _restoreForm are now
  tab-agnostic — no more per-mode branching when adding w field
- Add TAB_ACTIONS config map; list header renders one data-driven new-button instead of
  four conditional blocks; _editorTitle() extracts drawer title logic from the template
- Extract .btn-icon CSS primitive; .close-btn, .more-btn, .row-action-btn now declare
  only size/color overrides
Chat/event compatibility:
- Support both legacy (da-skills-lab-*) and new (da-skills-editor-*) event/sessionStorage
  namespaces so suggestion handoff and prompt-to-chat work during the migration window
- chat.js listens for both prompt-add-to-chat and prompt-send families with dedup guard
Prompt edit parity:
- Track _formPromptOriginalTitle so prompt rename upserts the correct row
- Track _formPromptIcon so the icon field is preserved on save
- Restore Add to Chat button in prompt rows and editor footer
Agent parity:
- Store and render aut ignored in UI)
- loadAgentPresets() handles both label/tools and name/mcpServers preset shapes
- New Agent path now creates /.da/agents/{id}.json via saveAgentPresetFile()
- Dedicated _renderAgentForm() / _onSaveAgent() with id, name, and starter preset
Generated Tools tab remains removed (unreachable by design); data still loads.
Add MCP tool listing and per-tool enable/disable controls with config persistence, pass MCP auth headers through chat/agent requests, and stabilize the tree throttle test timing assertion.
…cessibility

Normalize sheet boolean parsing for tool overrides and MCP enablement, make MCP cards keyboard-activatable, and stabilize unit tests against current skills-editor behavior.
@aem-code-sync
Copy link
Copy Markdown

aem-code-sync Bot commented Apr 28, 2026

Hello, I'm the AEM Code Sync Bot and I will run some actions to deploy your branch.
In case there are problems, just click the checkbox below to rerun the respective action.

  • Re-sync branch
Commits

@aem-code-sync aem-code-sync Bot temporarily deployed to feat/ew-skills-editor-recovered April 28, 2026 10:03 Inactive
Guard MCP card click/keydown activation so nested menu buttons do not trigger card open, and add regression tests for keyboard and click bubbling behavior.
Drop nx2/img/adobe.svg since it is not required for the skills-editor workstream.
…nd refresh

Render skills-editor immediately from per-site session snapshot, refresh data in the background with an auto-refresh progress indicator, lazy-load agents/MCP tools by tab, and keep orphan-skill sync off the critical path.
@anfibiacreativa
Copy link
Copy Markdown
Member Author

anfibiacreativa commented Apr 28, 2026

Closed in favor of #398

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant