Skip to content

fix(platform-core): never silently drop a perspective with an unknown groupId (#6646) - #6648

Merged
delchev merged 1 commit into
masterfrom
fix/perspective-group-silent-drop
Aug 10, 2026
Merged

fix(platform-core): never silently drop a perspective with an unknown groupId (#6646)#6648
delchev merged 1 commit into
masterfrom
fix/perspective-group-silent-drop

Conversation

@delchev

@delchev delchev commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #6646.

Problem

The perspective aggregator (platform-core/extension-services/modules/perspectives.mjs) places contributions into navigation groups by groupId. A groupId matching no declared group was discarded outright — so the group rendered empty, which is indistinguishable from "no module contributes here". No console warning, no server log line, nothing missing-looking in the UI.

The mypersonal shell rename (#6611/#6612) triggered exactly that: already-generated modules still declared groupId: 'my', and their personal pages vanished while every intermediate layer reported success (/services/core/extensions listed all of them with error=''; the aggregator answered GROUP id='personal' items=[]). The first hypotheses were all wrong — impersonation, identity mapping, roles, modules not bundled — because nothing anywhere said a perspective had been dropped.

Fix

Three layers, so the failure mode is removed rather than merely reported.

1. The aggregator never drops, and never stays quiet. An unmatched groupId is console.error-ed naming the perspective and the id (it reaches the platform log — the cheap half, worth having on its own), then the perspective is placed in:

  1. the extension point's default group — a group declaring isDefault: true, new;
  2. else the platform's undefined-group catch-all (unchanged behaviour);
  3. else handed back standalone, for the consuming shell to render.

undefined-group is deliberately not marked default, so a no-groupId perspective on application-perspectives keeps its current standalone handling and the shells' "Other" section.

2. The single-group shells own the placement. resources-personal / resources-partner / resources-admin each declare their one navigation group isDefault: true, and their generated perspectives (ui/{my,partner,admin}/*-perspective.js.template) no longer bake in the group id at all. This is request 3 of the issue: repeating a platform constant in every generated artifact is a coupling that can only ever drift, and a rename could then only be repaired by regenerating every module. A stale groupId: 'my' in an app built from already-published packages now lands in personal too — the fix reaches artifacts it cannot change.

3. The shells' catch-all covers every kind. The four Harmonia shells rescued only kind: 'PRIMARY' into "Other"; a generated personal page is kind: 'PERSONAL', matched no branch, and fell off the end of the forEach — in the very block whose comment says un-grouped perspectives are collected "instead of being silently dropped". The branch now rescues any kind and console.warns when it does.

Tests

  • PerspectiveGroupAggregationIT (new, HTTP-only, ~30 s) publishes perspectives with a stale groupId and with none at all, and asserts both come back inside the shell's group and neither standalone; a second test asserts the undefined-group fallback still applies where no default is declared. Verified to fail against the pre-fix aggregator (the stale/groupless perspective never appears) and pass with it.
  • IntentEmissionCoverageIT now asserts each shell perspective is inside its group (perspectives.find { it.id == 'personal' }.items.id) rather than merely present somewhere in the response, and that no group id is baked into the generated artifact. This is the issue's reusable lesson: for an artifact whose value is a constant the platform must agree with, assert the constant.

Both green locally (-Dit.test="PerspectiveGroupAggregationIT,IntentEmissionCoverageIT"), plus a full mvn -T 1C clean install -P quick-build.

🤖 Generated with Claude Code

… groupId (#6646)

The perspective aggregator places contributions into navigation groups by
`groupId`. A groupId matching no declared group was discarded outright, so the
group rendered empty - indistinguishable from "no module contributes here",
with no console warning and no server log line anywhere. The "My Work" ->
"Personal" rename triggered exactly that: every already-generated module still
declared `groupId: 'my'`, and its personal pages vanished while the extension
layer reported perfect health.

Three layers, so the failure mode is removed rather than reported:

- `perspectives.mjs` logs the unmatched groupId (naming the perspective and the
  id), then places the perspective in the extension point's DEFAULT group - one
  declaring `isDefault: true` - else the platform's `undefined-group` catch-all,
  else hands it back standalone for the consuming shell. It is never dropped.
  `undefined-group` is deliberately not marked default, so a no-groupId
  perspective on `application-perspectives` keeps its current standalone
  handling.
- the single-group shells (personal / partner / admin) declare their one group
  `isDefault: true` and their generated perspectives no longer bake in its id.
  Repeating a platform constant in every generated artifact is a coupling that
  can only drift, and a rename of it could only be repaired by regenerating
  every module.
- the four Harmonia shells' catch-all now rescues any `kind`, not just PRIMARY,
  and warns when it does. Enumerating kinds is how `PERSONAL` fell off the end
  of that chain despite a branch written to prevent silent drops.

Tests: `PerspectiveGroupAggregationIT` covers both placements (verified to fail
on the pre-fix aggregator); `IntentEmissionCoverageIT` now asserts each shell
perspective is INSIDE its group rather than merely present in the response, and
that no group id is baked into the generated artifact.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@delchev
delchev merged commit 6224b84 into master Aug 10, 2026
10 checks passed
@delchev
delchev deleted the fix/perspective-group-silent-drop branch August 10, 2026 10:26
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.

Personal shell silently discards a perspective whose groupId matches no group

1 participant