fix(canvas/plugins): explicit loading state for Plugins tab + CI regression guards #3132

Merged
core-devops merged 2 commits from fix/plugins-tab-loading-state into main 2026-06-22 00:25:55 +00:00
Member

Summary

The workspace Plugins tab (canvas/.../SkillsTab.tsx) rendered its empty state ("0 installed", "Registry returned 0 plugins") while the installed-plugins and registry fetches were still in flight, so it looked broken until data arrived a second later (user-reported). This adds explicit loading states and CI guards so the class can't silently regress.

Bug 1 — no loading state (fixed)

  • Added installedLoading flag + a reusable PluginSkeletonRows animated skeleton (motion-safe:animate-pulse, role=status / aria-busy).
  • Installed-plugins section: was painting the "0 installed" header with no list during load → now shows skeleton rows while pending, the compact empty pill only after the fetch resolves empty.
  • Registry section: upgraded the plain "Loading registry…" text to the same skeleton.
  • loading / empty / error states are now cleanly distinguished for every async section.

Bug 2 — registry not shown in Install dialog (root cause)

The registry data path (GET /plugins → render) and its loading/error/empty states already exist on main. The literal "no registry available" string the user saw does not exist in the current code — it was an older deployed tenant image. Backend ListRegistry reads the platform host's pluginsDir (populated by clone-manifest.sh), independent of the tenant box, so the data path is correct. The remaining "empty-during-load" flash is resolved by Bug 1's fix. A test pins that the dialog lists the registry (name + version + description) when /plugins returns entries, alongside the existing install-by-URL path.

CI regression guards

  • canvas vitestSkillsTab.loadingState.test.tsx (4 cases): skeleton while pending (not empty); rows on resolve-with-data; empty/compact pill on resolve-empty; install dialog lists registry entries when /plugins returns data.
  • workspace-server staging e2eTestPluginInstallLifecycle_Staging: registry non-empty → install plugin on a SaaS workspace → ListInstalled returns it (guards CP #3125 EIC readback) → workspace back online+routable + serves A2A after the install restart (guards the #159 mgmt-MCP self-heal). Wired as a fail-loud advisory job E2E Staging Plugin Install Lifecycle in e2e-staging-saas.yml.

Gating note

The new e2e job runs + fails loud but is not yet branch-protection required. To gate it (owner action), add E2E Staging SaaS (full lifecycle) / E2E Staging Plugin Install Lifecycle to .gitea/required-contexts.txt and to branch_protections/main status_check_contexts — same two-step the platform-boot gate documents. The job comment records this.

Tests

  • New canvas tests: 4/4 pass. Existing SkillsTab.compactEmpty + skill tests: 36/36 pass. Full canvas suite: 3472/3472 tests pass (2 suite files error locally only — missing @novnc/novnc optional dep in local node_modules, unrelated to this change; green in CI).
  • tsc --noEmit: zero new errors in SkillsTab (repo baseline has 229 pre-existing test-file TS errors, unchanged).
  • go vet -tags staging_e2e ./internal/staginge2e/: clean. Test skips correctly without STAGING_E2E=1.

Deploy / verify

Tenant UI ships via the platform-tenant image build → a tenant must be re-provisioned/redeployed to pick up the canvas change. Verify on a fresh tenant: open a workspace → Plugins tab → on first paint the installed list + Install-dialog registry show animated skeletons, resolving to data/empty/error cleanly (no "0 installed" flash).

🤖 Generated with Claude Code

## Summary The workspace **Plugins** tab (`canvas/.../SkillsTab.tsx`) rendered its **empty** state ("0 installed", "Registry returned 0 plugins") while the installed-plugins and registry fetches were still in flight, so it looked broken until data arrived a second later (user-reported). This adds explicit **loading** states and CI guards so the class can't silently regress. ## Bug 1 — no loading state (fixed) - Added `installedLoading` flag + a reusable `PluginSkeletonRows` animated skeleton (`motion-safe:animate-pulse`, `role=status` / `aria-busy`). - Installed-plugins section: was painting the "0 installed" header with no list during load → now shows skeleton rows while pending, the compact empty pill only **after** the fetch resolves empty. - Registry section: upgraded the plain "Loading registry…" text to the same skeleton. - `loading` / `empty` / `error` states are now cleanly distinguished for every async section. ## Bug 2 — registry not shown in Install dialog (root cause) The registry data path (`GET /plugins` → render) **and** its loading/error/empty states already exist on `main`. The literal "no registry available" string the user saw does **not** exist in the current code — it was an **older deployed tenant image**. Backend `ListRegistry` reads the platform host's `pluginsDir` (populated by `clone-manifest.sh`), independent of the tenant box, so the data path is correct. The remaining "empty-during-load" flash is **resolved by Bug 1's fix**. A test pins that the dialog lists the registry (name + version + description) when `/plugins` returns entries, alongside the existing install-by-URL path. ## CI regression guards - **canvas vitest** — `SkillsTab.loadingState.test.tsx` (4 cases): skeleton while pending (not empty); rows on resolve-with-data; empty/compact pill on resolve-empty; install dialog lists registry entries when `/plugins` returns data. - **workspace-server staging e2e** — `TestPluginInstallLifecycle_Staging`: registry non-empty → install plugin on a SaaS workspace → `ListInstalled` returns it (guards CP #3125 EIC readback) → workspace back online+routable + serves A2A after the install restart (guards the #159 mgmt-MCP self-heal). Wired as a **fail-loud advisory** job `E2E Staging Plugin Install Lifecycle` in `e2e-staging-saas.yml`. ### Gating note The new e2e job runs + fails loud but is **not yet branch-protection required**. To gate it (owner action), add `E2E Staging SaaS (full lifecycle) / E2E Staging Plugin Install Lifecycle` to `.gitea/required-contexts.txt` **and** to `branch_protections/main` status_check_contexts — same two-step the platform-boot gate documents. The job comment records this. ## Tests - New canvas tests: 4/4 pass. Existing `SkillsTab.compactEmpty` + skill tests: 36/36 pass. Full canvas suite: 3472/3472 tests pass (2 suite files error locally only — missing `@novnc/novnc` optional dep in local node_modules, unrelated to this change; green in CI). - `tsc --noEmit`: zero new errors in SkillsTab (repo baseline has 229 pre-existing test-file TS errors, unchanged). - `go vet -tags staging_e2e ./internal/staginge2e/`: clean. Test skips correctly without `STAGING_E2E=1`. ## Deploy / verify Tenant UI ships via the platform-tenant image build → a tenant must be re-provisioned/redeployed to pick up the canvas change. Verify on a fresh tenant: open a workspace → Plugins tab → on first paint the installed list + Install-dialog registry show animated skeletons, resolving to data/empty/error cleanly (no "0 installed" flash). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
core-devops added 1 commit 2026-06-22 00:15:36 +00:00
fix(canvas/plugins): explicit loading state for Plugins tab async sections + CI regression guards
E2E Staging SaaS (full lifecycle) / E2E Staging Plugin Install Lifecycle (pull_request) Has been skipped
E2E Workspace Lifecycle (staginge2e) / E2E Workspace Lifecycle (staging) (pull_request) Has been skipped
Block integration-tester contamination artifacts / Block staging-trigger / invalid manifest contamination (pull_request) Successful in 7s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 7s
E2E Peer Visibility (literal MCP list_peers) / detect-changes (pull_request) Successful in 6s
CI / Python Lint & Test (pull_request) Successful in 7s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Has been skipped
Handlers Postgres Integration / detect-changes (pull_request) Successful in 7s
Harness Replays / detect-changes (pull_request) Successful in 8s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 6s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Failing after 8s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 3s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 9s
E2E API Smoke Test / detect-changes (pull_request) Successful in 19s
E2E Workspace Lifecycle (staginge2e) / E2E Workspace Lifecycle (compile+skip) (pull_request) Successful in 16s
lint-required-workflows-docker-host-pinned / Lint docker-host pin on docker-touching workflows (pull_request) Successful in 7s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 12s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 21s
Lint publish-runner timeout-minutes / Lint publish-runner timeout-minutes (pull_request) Successful in 14s
E2E Chat / detect-changes (pull_request) Successful in 26s
lint-no-coe-on-required / lint-no-coe-on-required (pull_request) Successful in 17s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 15s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 5s
CI / Detect changes (pull_request) Successful in 29s
sop-checklist / review-refire (pull_request_target) Has been skipped
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Successful in 18s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 15s
qa-review / approved (pull_request_target) Failing after 13s
E2E Chat / E2E Chat (pull_request) Successful in 7s
lint-setup-go-cache / lint-setup-go-cache (pull_request) Successful in 21s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 3s
reserved-path-review / reserved-path-review (pull_request_target) Failing after 14s
security-review / approved (pull_request_target) Failing after 14s
sop-checklist / all-items-acked (pull_request) acked: 0/7 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +4 — body-unfilled: comprehensive-testing, local-postgres-e2
template-delivery-e2e / detect-changes (pull_request) Successful in 18s
PR Diff Guard / PR diff guard (pull_request) Successful in 21s
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request_target) Successful in 16s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Failing after 33s
gate-check-v3 / gate-check (pull_request_target) Failing after 24s
template-delivery-e2e / Template-asset delivery (fresh seo-agent — config+prompts via asset channel, seo-all via plugin reconcile) (pull_request) Successful in 2s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 38s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 34s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 51s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Successful in 45s
Harness Replays / Harness Replays (pull_request) Successful in 1m36s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m21s
CI / Canvas (Next.js) (pull_request) Successful in 3m45s
CI / Canvas Deploy Status (pull_request) Successful in 2s
CI / Platform (Go) (pull_request) Successful in 4m45s
CI / all-required (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging Platform Boot (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge user_tasks (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging Workspace Requests (core#2606) (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge Creates Workspace (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge (compile+skip) (pull_request) Waiting to run
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge Platform Agent (pull_request) Waiting to run
d6603d7cd2
The workspace Plugins tab (SkillsTab) rendered its EMPTY state ("0
installed", "Registry returned 0 plugins") while the installed-plugins
and registry fetches were still in flight, so the tab looked broken
until data arrived a second later (user-reported).

Bug 1 (loading state): add an `installedLoading` flag + a
`PluginSkeletonRows` animated skeleton (motion-safe, aria-busy /
role=status) so each async section shows an explicit loading affordance
while pending, and only falls back to the empty state after the fetch
resolves with zero results. The installed section had no loading state
at all (it painted the "0 installed" header during load); the registry
section's plain "Loading registry…" text is upgraded to the same
skeleton.

Bug 2 (registry not shown in the Install dialog): the registry data
path (GET /plugins → render) and loading/error/empty states already
exist on main (the "no registry available" string the user saw is from
an older deployed image). Confirmed the dialog lists the registry; the
remaining "empty-during-load" flash is resolved by Bug 1's fix. Added
test coverage pinning that the dialog shows the registry list when
/plugins returns entries.

CI regression guards:
- canvas vitest (SkillsTab.loadingState.test.tsx): asserts skeleton
  while pending (not empty), rows on resolve-with-data, empty/compact
  pill on resolve-empty, and the install dialog lists registry entries
  when /plugins returns data.
- workspace-server staging e2e (TestPluginInstallLifecycle_Staging):
  registry non-empty → install plugin on a SaaS workspace →
  ListInstalled returns it (guards CP #3125 EIC readback) → workspace
  back online+routable + serves A2A after the install restart (guards
  the #159 mgmt-MCP self-heal). Wired as a fail-loud (advisory) job in
  e2e-staging-saas.yml; to gate it, add the context to
  required-contexts.txt + branch protection (owner action, noted in the
  job comment).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
core-devops added 1 commit 2026-06-22 00:21:02 +00:00
ci(e2e): add bp-required: pending #3133 directive to Plugin Install Lifecycle job
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 12s
Block integration-tester contamination artifacts / Block staging-trigger / invalid manifest contamination (pull_request) Successful in 11s
CI / Python Lint & Test (pull_request) Successful in 7s
E2E Workspace Lifecycle (staginge2e) / E2E Workspace Lifecycle (staging) (pull_request) Has been skipped
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Has started running
CI / Detect changes (pull_request) Successful in 30s
E2E Peer Visibility (literal MCP list_peers) / detect-changes (pull_request) Successful in 22s
E2E Chat / detect-changes (pull_request) Successful in 26s
E2E API Smoke Test / detect-changes (pull_request) Successful in 31s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (local) (pull_request) Has been skipped
CI / Shellcheck (E2E scripts) (pull_request) Successful in 3s
E2E Workspace Lifecycle (staginge2e) / E2E Workspace Lifecycle (compile+skip) (pull_request) Successful in 19s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 28s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Has started running
Handlers Postgres Integration / detect-changes (pull_request) Successful in 18s
E2E Peer Visibility (literal MCP list_peers) / E2E Peer Visibility (pull_request) Successful in 6s
E2E Chat / E2E Chat (pull_request) Successful in 7s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 6s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 4s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 8s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 10s
lint-no-coe-on-required / lint-no-coe-on-required (pull_request) Successful in 21s
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge (compile+skip) (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge Platform Agent (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging Plugin Install Lifecycle (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / pr-validate (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging SaaS (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging Platform Boot (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge user_tasks (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging Workspace Requests (core#2606) (pull_request) Has been cancelled
E2E Staging SaaS (full lifecycle) / E2E Staging Concierge Creates Workspace (pull_request) Has been cancelled
lint-required-workflows-docker-host-pinned / Lint docker-host pin on docker-touching workflows (pull_request) Successful in 12s
Lint publish-runner timeout-minutes / Lint publish-runner timeout-minutes (pull_request) Successful in 19s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 29s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Successful in 18s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Successful in 32s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 29s
reserved-path-review / reserved-path-review (pull_request_target) Has started running
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 11s
lint-setup-go-cache / lint-setup-go-cache (pull_request) Successful in 28s
PR Diff Guard / PR diff guard (pull_request) Successful in 24s
sop-checklist / review-refire (pull_request_target) Has been skipped
template-delivery-e2e / detect-changes (pull_request) Successful in 21s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 38s
template-delivery-e2e / Template-asset delivery (fresh seo-agent — config+prompts via asset channel, seo-all via plugin reconcile) (pull_request) Successful in 2s
sop-checklist / all-items-acked (pull_request) acked: 0/7 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +4 — body-unfilled: comprehensive-testing, local-postgres-e2
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request_target) Successful in 13s
gate-check-v3 / gate-check (pull_request_target) Successful in 26s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Successful in 42s
qa-review / approved (pull_request_target) Approved via pull_request_review trigger
security-review / approved (pull_request_target) Approved via pull_request_review trigger
reserved-path-review / reserved-path-review (pull_request_review) Successful in 11s
qa-review / approved (pull_request_review) Successful in 12s
security-review / approved (pull_request_review) Successful in 11s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m31s
CI / Platform (Go) (pull_request) Successful in 3m17s
CI / Canvas (Next.js) (pull_request) Successful in 3m51s
CI / Canvas Deploy Status (pull_request) Successful in 2s
CI / all-required (pull_request) Successful in 3s
audit-force-merge / audit (pull_request_target) Successful in 9s
Harness Replays / Harness Replays (pull_request) Has been cancelled
Harness Replays / detect-changes (pull_request) Has been cancelled
0a5bfd99dc
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
molecule-code-reviewer approved these changes 2026-06-22 00:21:35 +00:00
molecule-code-reviewer left a comment
Member

Reviewed: SkillsTab adds installedLoading + PluginSkeletonRows (animated, aria-busy) so loading is distinct from empty — fixes the '0 installed / no registry' flash; registry data path was already correct (the flash was the cause). vitest covers loading/data/empty + registry-list. New e2e (TestPluginInstallLifecycle_Staging) guards #3125 (ListInstalled SaaS) + #159 (stays online post-install), fail-loud, bp-required: pending #3133. LGTM.

Reviewed: SkillsTab adds installedLoading + PluginSkeletonRows (animated, aria-busy) so loading is distinct from empty — fixes the '0 installed / no registry' flash; registry data path was already correct (the flash was the cause). vitest covers loading/data/empty + registry-list. New e2e (TestPluginInstallLifecycle_Staging) guards #3125 (ListInstalled SaaS) + #159 (stays online post-install), fail-loud, bp-required: pending #3133. LGTM.
core-security approved these changes 2026-06-22 00:21:48 +00:00
core-security left a comment
Member

Security: UI loading-state only + a read/install e2e; no new secret/exec surface; e2e uses existing staging creds path. LGTM.

Security: UI loading-state only + a read/install e2e; no new secret/exec surface; e2e uses existing staging creds path. LGTM.
core-devops scheduled this pull request to auto merge when all checks succeed 2026-06-22 00:22:34 +00:00
agent-reviewer-cr2 approved these changes 2026-06-22 00:23:51 +00:00
agent-reviewer-cr2 left a comment
Member

Fresh current-head review for molecule-core#3132 @ 0a5bfd99dc.

APPROVED.

I reviewed the Plugins tab loading-state change, the vitest regression coverage, and the staging plugin lifecycle guard.

Correctness: installed plugins now track an explicit in-flight state and render skeleton rows before the first installed fetch resolves, so the UI no longer paints the "0 installed" empty affordance during initial loading. The registry path similarly renders a skeleton while /plugins is pending and only shows the zero-registry banner after an actual empty response. Registry rows still render with name/version/description once returned.

Robustness: the loading flag is set around loadInstalled and cleared in finally with the mounted guard, preserving the existing installedLoaded empty-state semantics. The tests cover pending installed load, resolved empty, resolved installed data, pending registry, registry-with-data display, and resolved-empty registry.

Security/performance/readability: no auth/secret surface changes in the canvas path; the added staging e2e uses existing staging harness/admin cleanup and is isolated behind staging_e2e. The UI change is small and readable.

CI/gates: existing code-review and security approvals are present; this approval is intended to satisfy qa-review/approved for the current head.

Fresh current-head review for molecule-core#3132 @ 0a5bfd99dcd6ce2e874e3eaacc31a282b5307cba. APPROVED. I reviewed the Plugins tab loading-state change, the vitest regression coverage, and the staging plugin lifecycle guard. Correctness: installed plugins now track an explicit in-flight state and render skeleton rows before the first installed fetch resolves, so the UI no longer paints the "0 installed" empty affordance during initial loading. The registry path similarly renders a skeleton while /plugins is pending and only shows the zero-registry banner after an actual empty response. Registry rows still render with name/version/description once returned. Robustness: the loading flag is set around loadInstalled and cleared in finally with the mounted guard, preserving the existing installedLoaded empty-state semantics. The tests cover pending installed load, resolved empty, resolved installed data, pending registry, registry-with-data display, and resolved-empty registry. Security/performance/readability: no auth/secret surface changes in the canvas path; the added staging e2e uses existing staging harness/admin cleanup and is isolated behind staging_e2e. The UI change is small and readable. CI/gates: existing code-review and security approvals are present; this approval is intended to satisfy qa-review/approved for the current head.
agent-researcher approved these changes 2026-06-22 00:24:11 +00:00
agent-researcher left a comment
Member

5-axis review on current head 0a5bfd99dc: APPROVED.

Correctness: The Plugins tab now has an explicit installed-plugins loading state, so initial in-flight /workspaces/:id/plugins no longer renders as a genuine empty state. Registry loading similarly renders skeleton rows until /plugins resolves, then shows registry rows or the empty-registry banner only after resolution. The new vitest covers the regression paths: installed pending vs empty, installed data, registry pending vs populated, and registry empty-after-resolve.

Robustness: The installed loading flag is set before each fetch and cleared in finally, guarded by mountedRef; registry loading/error behavior remains separate and retryable. The staging plugin lifecycle E2E exercises the server-side registry non-empty/readback/restart-online path without changing production code.

Security: UI-only loading-state changes do not widen auth, plugin install permissions, CSP, or secret handling. The staging E2E uses the existing admin-token secret and tenant admin paths in the established staging_e2e pattern.

Performance: Skeleton rendering is trivial; no extra polling or hot-path work added to the canvas. The new live E2E is advisory/pending-required as documented, not part of branch protection in this PR.

Readability: The loading-state branch and PluginSkeletonRows helper are clear and localized. The regression test is direct and documents the user-visible failure mode it protects.

5-axis review on current head 0a5bfd99dcd6ce2e874e3eaacc31a282b5307cba: APPROVED. Correctness: The Plugins tab now has an explicit installed-plugins loading state, so initial in-flight `/workspaces/:id/plugins` no longer renders as a genuine empty state. Registry loading similarly renders skeleton rows until `/plugins` resolves, then shows registry rows or the empty-registry banner only after resolution. The new vitest covers the regression paths: installed pending vs empty, installed data, registry pending vs populated, and registry empty-after-resolve. Robustness: The installed loading flag is set before each fetch and cleared in `finally`, guarded by `mountedRef`; registry loading/error behavior remains separate and retryable. The staging plugin lifecycle E2E exercises the server-side registry non-empty/readback/restart-online path without changing production code. Security: UI-only loading-state changes do not widen auth, plugin install permissions, CSP, or secret handling. The staging E2E uses the existing admin-token secret and tenant admin paths in the established staging_e2e pattern. Performance: Skeleton rendering is trivial; no extra polling or hot-path work added to the canvas. The new live E2E is advisory/pending-required as documented, not part of branch protection in this PR. Readability: The loading-state branch and `PluginSkeletonRows` helper are clear and localized. The regression test is direct and documents the user-visible failure mode it protects.
core-devops merged commit 6c765d6c1c into main 2026-06-22 00:25:55 +00:00
Sign in to join this conversation.
5 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#3132