Skip to content

feat(skills): add Enterprise Context tab with the governance MFE - #53

Merged
AlexRRR merged 5 commits into
mainfrom
egovcxt
Sep 8, 2026
Merged

feat(skills): add Enterprise Context tab with the governance MFE#53
AlexRRR merged 5 commits into
mainfrom
egovcxt

Conversation

@AlexRRR

@AlexRRR AlexRRR commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

What this does

Adds an Enterprise Context tab to the Skills Editor that embeds the Experience Governance micro-frontend (MFE from here on).

Screenshot 2026-08-03 at 10 41 58 Screenshot 2026-08-03 at 10 34 21

Why

Enterprise Context has only ever been reachable from Experience Manager, via aem-home-ui. Anyone who wanted to manage it had to leave Experience Workspace and go somewhere else to do it. This brings that UI in here so it reads as part of the workspace instead of a separate destination.

Embedding the micro-frontend rather than porting the UI keeps a second benefit that matters to both sides: the MFE stays on its own release cadence. Changes its maintainers ship appear in Experience Manager and here at the same time, with no follow-up work in this repo.

How

The MFE already ships a shell-free embed entry that expects no Unified Shell, so this targets that. The host side is a vanilla re-implementation of the MessageRpc protocol from @assets/microfrontend (handshake, reactSetProps, reactCallback — see src/message/MessageRpc.ts), which avoids taking on Unified Shell as a dependency.

  • Env resolution. qa/stage/prod bundle picked from da-live's hostname, with ?egov=local for a local MFE dev server (same convention as ?nx= / ?da-admin=). The same env selects the MFE's env prop so bundle and backend API match.
  • Deep linking. MFE path read from and written to ?egovPath=, so a governance screen is shareable and survives reload.
  • Auth. Reads the existing IMS session. Since props carry a live bearer token, postMessage is pinned to the embed's origin and event.origin validated inbound, rather than '*'.

How to test

URL for testing:

That page renders the editor and the new tab, but it has no IMS session (da-live's shell provides it), so the MFE itself can't authenticate there. For the working feature, run the stack locally:

cd da-live       && aem up --port=3000 --no-open
cd da-nx         && npm run local
cd ew-extensions && aem up --port=3001 --no-open

Then open http://localhost:3000/apps/skills?nx=local#/<org>/<site> and pick the Enterprise Context tab.

  • Deep link: append ?egovPath=/brands/<id> and reload — should land on that screen.
  • Other MFE bundles: ?egov=qa|local.

Known limitations

Scoped out deliberately to keep this PR to a reviewable size. Both are planned as follow-up PRs.

  • Browser Back/Forward doesn't navigate within the tab. Path writes always replace, so no history entries are added. Pushing them requires a popstate listener that feeds the path back into the MFE (it owns its own router and only sees the host URL through props). Without that, Back would change the URL while the iframe kept showing the same screen. This is the next PR; note the MFE's usual parent doesn't wire it up either.
  • No loading/error UI if the MFE bundle is slow or unreachable, so the iframe stays blank.

Checks

  • npm test — 228 unit tests pass, including new coverage for the bridge's handshake/props/callback protocol and the ?egovPath= round trip.
  • npm run lint — clean.
  • Verified live in the browser: the embedded MFE makes authenticated calls to the real governance API and renders, and deep links land on the right screen.

Auth was checked with the darkalley IMS client against a single org. Other orgs and anonymous sessions haven't been exercised yet; the bridge omits imsToken/imsOrg when it can't read them.

Iframe the Experience Governance MFE's shell-free embed entry and bridge it
with a vanilla re-implementation of @assets/microfrontend's MessageRpc, so
no Unified Shell dependency is needed.

- resolve the qa/stage/prod CDN bundle from da-live's hostname, with an
  ?egov=local override for a local MFE dev server; the same env picks the
  MFE's `env` prop so bundle and backend API always match
- deep-link via ?egovPath=, so a governance screen is shareable and survives
  reload. Writes always replace: adding history entries first needs a
  popstate listener feeding the path back into the MFE, so browser
  Back/Forward does not navigate within the tab yet
- pin postMessage to the embed's origin and validate event.origin inbound,
  since the props payload carries a live IMS bearer token
- unit-test the handshake, props and callback protocol plus the ?egovPath=
  round trip

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@aem-code-sync

aem-code-sync Bot commented Aug 3, 2026

Copy link
Copy Markdown

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

  • Re-run all PSI checks
  • Re-run failed PSI checks
  • Re-sync branch
Commits

@aem-code-sync

aem-code-sync Bot commented Aug 3, 2026

Copy link
Copy Markdown
Page Scores Audits Google
📱 /blocks/skills/skills.html PERFORMANCE A11Y SEO BEST PRACTICES SI FCP LCP TBT CLS PSI
🖥️ /blocks/skills/skills.html PERFORMANCE A11Y SEO BEST PRACTICES SI FCP LCP TBT CLS PSI

Comment thread blocks/skills/utils/egov-bridge.js Outdated
An unrecognized or missing env resolved to stage in three places: the
bridge's props default, the embed bundle URL, and the env prop handed to
the MFE. That fails unsafe -- a user on da.live could silently get stage
data in a production-looking UI. All three now fall back to prod, which
also matches what resolveEgovEnv already does for unknown hostnames.
Comment thread blocks/skills/nx-skills-editor.js
Comment thread blocks/skills/utils/egov-embed.js
Comment thread blocks/skills/utils/egov-bridge.js
Comment thread blocks/skills/utils/egov-bridge.js
Comment thread blocks/skills/nx-skills-editor.js Outdated
Comment thread test/unit/utils/egov-bridge.test.js
Comment thread blocks/skills/constants.js Outdated
Comment thread blocks/skills/utils/egov-embed.js Outdated
Test coverage for the two security-relevant branches:

- Env resolution had none, and its fallbacks had just changed in 7c6975e
  under review. Adds cases for the ?egov= override (accepted and rejected),
  the localhost / *.aem.page stage split, and unknown host to prod. The
  `|| PROD` fallbacks themselves are unreachable, since resolveEgovEnv only
  ever returns an allow-listed value, so the tests cover the reachable route
  and say so rather than implying the defensive line is guarded. Also pins
  two ways the guard could be loosened later: ?egov=PROD stays rejected (the
  allow-list is lowercase-only) and lookalike hosts miss the suffix check.

- The bridge's event.origin check is what keeps the IMS token in, and nothing
  guarded it. Adds a test that a message from another origin is dropped,
  verified by deleting the check and watching it fail.

Code:

- Stop advertising onToast, which reactCallback never handled. Claiming a
  callback the host ignores can make the MFE suppress its own in-frame UI for
  it, turning a dropped toast into silence.
- Grant clipboard-write on the iframe, matching every sibling embed. The frame
  is cross-origin, where clipboard-write defaults to self, so any copy inside
  it fails silently without this.
- Use TAB_CONTEXT/TAB_MEMORY instead of 8 raw tab strings; the file imported
  from constants.js but never pulled the tab ids in, so it could drift from
  renderers.js.
- Name the egov/egovPath query params instead of repeating them inline.
- Fix the TAB_CONTEXT comment, which said the label was "Context".
- Document colorScheme/locale: 'light' matches the host, which skills.html
  pins to light until dark mode lands, so the pin and the prop have to be
  un-pinned together.
@AlexRRR

AlexRRR commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for taking the time on this one. It was a genuinely careful read, and it made the PR better.

All eight are done in f0e33ac:

Tests

  • Env resolution now has cases for ?egov= (valid and rejected), the localhost / *.aem.page split, and unknown host to prod. One caveat: the || PROD fallbacks can't actually be reached, because resolveEgovEnv only ever returns an allow-listed value. So I test the route that is reachable (an unknown hostname) and left a comment saying so, rather than pretending that line is covered.
  • Cross-origin messages are now tested. I deleted the origin check first to make sure the test really fails without it.

Code

  • onToast was an oversight, not a stub. Removed from callbacks.
  • Added allow="clipboard-write *". Being honest, I matched the other embeds. Cross-origin frames block clipboard by default, so either it was quietly broken or the grant costs nothing.
  • Tab constants: you were right, and there were 8 raw strings rather than 1. The file imports from constants.js but never pulled in TAB_CONTEXT/TAB_MEMORY. Unrelated to the label comment, though.
  • egov and egovPath are named constants now.
  • Label comment fixed.
  • colorScheme and locale are commented. Turns out skills.html already pins the whole editor to light until dark mode lands, so 'light' actually matches the host instead of ignoring it. The comment notes that the pin and the prop have to be unpinned together.

Comment thread blocks/skills/nx-skills-editor.js Outdated

@fmeschbe fmeschbe left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am supportive of this addition in principle to offer access to Enterprise Context from within Experience Workspace. This is a first step in to the right direction. This being the first step, actual integration may change as we evolve Experience Workspace.

I think the Experience Workspace extensibility model at this point is much too intrusive and requires to many overlapping changes as exhibited by this PR. However, this is not this PR's fault, but rather the problem of the current extensibility model (and yes, this is my actual human wording ;-) ).

Going forward, Experience Workspace extensibility should be constructed in such a way to make plugs much easier and straight forward. However, I could also imagine that there may be more to it than just making extensions more pluggable.

@hannessolo

Copy link
Copy Markdown

I think the Experience Workspace extensibility model at this point is much too intrusive and requires to many overlapping changes as exhibited by this PR.

Keep in mind that this repository IS an extension being loaded into experience workspace. So everything this PR is modifying is part of the extension, not the core experience workspace code.

Whether the skills mgmt UI stays an extension, moves into core EW or even moves into the CMA bridge code will be a separate question. When we discuss that question, we'll also have to decide how we make the skills mgmt UI extensible, so a PR like this can be less intrusive.

@andreituicu

Copy link
Copy Markdown

@anfibiacreativa May I please ask you to have a second look at this PR to see if your feedback was addressed by Alejandro and we can proceed with merging?
We still require a code owner approval for being able to merge.

Comment thread blocks/skills/renderers.js Outdated
The egov MFE iframe only ever loads a single trusted governance origin,
so allow="clipboard-write *" grants more than needed. Dropping the `*`
defaults the allowlist to 'src', following least-privilege.
Per review, keep this PR focused on the Enterprise Context tab. The
TAB_MEMORY constant swap is orthogonal cleanup and can land separately.
@AlexRRR
AlexRRR merged commit 36b0108 into main Sep 8, 2026
5 checks passed
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.

5 participants