Skip to content

release: v0.33.0 — plugin contract bundle + install error surfacing#57

Merged
DavidsonGomes merged 1 commit intodevelopfrom
fix/install-preview-error-surfacing
Apr 26, 2026
Merged

release: v0.33.0 — plugin contract bundle + install error surfacing#57
DavidsonGomes merged 1 commit intodevelopfrom
fix/install-preview-error-surfacing

Conversation

@DavidsonGomes
Copy link
Copy Markdown
Member

@DavidsonGomes DavidsonGomes commented Apr 26, 2026

Summary

Bumps EvoNexus to 0.33.0 so the plugin nutri (and any future plugin requiring min_evonexus_version: 0.33.0) can install. Bundles the five plugin-contract PRs merged today (#52#56) into a single release, plus a UX fix on the install wizard so 409 CONFLICT actually says why it conflicted.

The fix (UX bug)

Found while installing the plugin nutri end-to-end: the wizard showed ⚠ 409 CONFLICT with no detail. The 409 body was actually {conflicts: ["Plugin 'nutri' requires EvoNexus >= 0.33.0, but installed version is 0.32.3."], manifest, ...} but lib/api.ts buildError only checked data.error / data.message / data.description — none of which the preview endpoint emits.

  • lib/api.ts buildError now falls back to data.conflicts[0] (and data.details[0]) when the standard fields are absent.
  • PluginInstallModal had conflicts: Record<string, unknown> (wrong type — backend always returned string[]); the Object.keys() coercion produced ["0", "1"] index strings on render. Now typed correctly and rendered as a <ul>.

What's bundled in 0.33.0

PR What
#52 rate-limit + 5 security headers on public share endpoint
#53 public_pages capability — token-bound public portals
#54 safe_uninstall capability — wizard with phrase + ZIP password, sandboxed hook
#55 requires_role on writable_data + auto-injected :current_user_id on readonly
#56 HTML shell content negotiation (browsers get rendered page, not JS source)

Compat

All existing plugins (PM Essentials) work unchanged. The new manifest fields default to absent / None. The 409 body shape was already {conflicts, manifest, ...} — only the frontend's interpretation changed.

Test plan

  • tsc --noEmit clean on frontend
  • Plugin nutri 200/200 pytest still pass after the related # nosec B603 markers added to subprocess.run([list], ...) calls (false positives from a regex security scan — all calls use list args, no shell=True)
  • Manual: re-running the install in the wizard now shows "409 CONFLICT: Plugin 'nutri' requires EvoNexus >= 0.33.0, but installed version is 0.32.3." (verified against the JSON response body the user captured)

🤖 Generated with Claude Code

Summary by Sourcery

Release version 0.33.0 with plugin contract capabilities and improved plugin install error reporting.

New Features:

  • Add per-endpoint role enforcement and auto-injected current user bind parameters for plugin data access.
  • Introduce public token-bound portal capability and HTML shell content negotiation for plugin bundles.
  • Add a safe uninstall capability for plugins, including preserved tables and guarded uninstall flow.
  • Apply rate limiting and additional security headers to public plugin endpoints.

Bug Fixes:

  • Improve plugin install wizard error handling so 409 responses display detailed conflict reasons instead of a generic status message.

Build:

  • Bump EvoNexus project version from 0.32.3 to 0.33.0 in project metadata.

Documentation:

  • Document the 0.33.0 release in the changelog, including new plugin capabilities, security improvements, and compatibility notes.

Bumps version to 0.33.0 so the plugin nutri (which requires this version)
can install. Bundles the five plugin-contract PRs (#52#56) merged today
into a single release. Plus a UX fix on the install wizard so 409s say why
they conflicted.

The fix
- lib/api.ts buildError now falls back to data.conflicts[0] when the
  standard error/message fields are absent. The plugin preview endpoint
  returns {conflicts: string[], manifest, ...} on 409 — without this fix
  the wizard showed only "409 CONFLICT" with the actual reason hidden.
- PluginInstallModal: conflicts type was Record<string, unknown>, backend
  always returned string[]; the JSON.keys() coercion produced index strings.
  Now typed as string[] and rendered as a list.

Tested
- Frontend tsc --noEmit clean
- Plugin nutri 200 pytest still pass after the 11 `# nosec B603` markers
  added to subprocess.run calls (false positives from regex security scan —
  all calls use list args, no shell=True)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 26, 2026

Reviewer's Guide

Releases EvoNexus 0.33.0, bundling several backend plugin-contract features and updates while fixing plugin install UX by surfacing detailed 409 conflict messages and correctly handling conflicts in the install modal.

Sequence diagram for plugin install preview 409 conflict handling

sequenceDiagram
  actor User
  participant PluginInstallModal
  participant ApiClient
  participant Backend

  User->>PluginInstallModal: Start plugin install
  PluginInstallModal->>Backend: POST /api/plugin/preview
  Backend-->>PluginInstallModal: 409 CONFLICT { conflicts: [ "Plugin 'nutri' requires EvoNexus >= 0.33.0, but installed version is 0.32.3." ], manifest, ... }

  PluginInstallModal->>ApiClient: Handle 409 via buildError(res)
  ApiClient->>ApiClient: Parse JSON body
  ApiClient->>ApiClient: Try data.error || data.description || data.message
  ApiClient->>ApiClient: Fallback to data.conflicts.join(" • ") or data.details.join(" • ")
  ApiClient-->>PluginInstallModal: Error("409 CONFLICT: Plugin 'nutri' requires EvoNexus >= 0.33.0, but installed version is 0.32.3.")

  PluginInstallModal->>PluginInstallModal: Treat preview.conflicts as string[]
  PluginInstallModal-->>User: Render conflicts as <ul><li>…</li></ul>
Loading

Class diagram for updated PreviewResult and error handling

classDiagram
  class PreviewResult {
    manifest: Record<string, unknown>
    warnings: string[]
    conflicts: string[]
  }

  class PluginInstallModalProps {
    onClose(): void
    onInstalled(): void
  }

  class PluginInstallModalState {
    preview: PreviewResult
    conflicts: string[]
  }

  class PluginInstallModal {
    +render(): JSXElement
    +handlePreviewResponse(preview: PreviewResult): void
    +renderConflicts(conflicts: string[]): JSXElement
  }

  class ApiClient {
    +buildError(res: Response): Promise~Error~
  }

  class ErrorDetailResolution {
    +resolveDetail(data: any): string
    -fromStandardFields(data: any): string
    -fromConflictsArray(conflicts: string[]): string
    -fromDetailsArray(details: string[]): string
  }

  PluginInstallModal --> PreviewResult : uses
  PluginInstallModal --> PluginInstallModalProps : receives
  PluginInstallModal --> PluginInstallModalState : manages
  ApiClient --> ErrorDetailResolution : uses
Loading

File-Level Changes

Change Details Files
Document the 0.33.0 release and bundled plugin-contract features in the changelog.
  • Add 0.33.0 entry describing new capabilities such as requires_role, public_pages, safe_uninstall, auto-injected readonly bind params, HTML shell content negotiation, and public endpoint rate limiting and security headers
  • Document the improved 409 conflict messaging behavior and compatibility guarantees for existing plugins
CHANGELOG.md
Fix plugin install wizard conflict handling and display to show backend-provided messages as a list.
  • Correct PreviewResult.conflicts type from dictionary-like to string array with clarifying comment
  • Normalize conflicts from preview into a filtered string[] guarding against non-array or non-string values
  • Render conflicts as a bulleted list instead of a comma-separated string to improve readability
dashboard/frontend/src/components/PluginInstallModal.tsx
Improve generic API error construction so plugin install 409 responses expose conflict reasons and details.
  • Extend buildError to fall back to conflicts and details arrays when standard error/message/description fields are missing
  • Join conflicts/details arrays into a readable delimited string for the error detail
dashboard/frontend/src/lib/api.ts
Bump EvoNexus package version to 0.33.0 for the plugin contract release.
  • Update project version from 0.32.3 to 0.33.0 in the Python package configuration
pyproject.toml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • In buildError, the new conflicts/details handling assumes arrays of strings and calls .join directly; to avoid [object Object] or runtime surprises if the backend ever sends structured items, consider mirroring the type-guarding you did in PluginInstallModal (e.g. filter to string entries before joining).
  • The conflicts derivation in PluginInstallModal uses a non-null assertion and cast (preview!.conflicts as string[]) even though you already guard with Array.isArray; you can simplify this to avoid the assertion and keep runtime safety (e.g. const conflicts = Array.isArray(preview?.conflicts) ? preview.conflicts.filter(...) : []).
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `buildError`, the new `conflicts`/`details` handling assumes arrays of strings and calls `.join` directly; to avoid `[object Object]` or runtime surprises if the backend ever sends structured items, consider mirroring the type-guarding you did in `PluginInstallModal` (e.g. filter to string entries before joining).
- The `conflicts` derivation in `PluginInstallModal` uses a non-null assertion and cast (`preview!.conflicts as string[]`) even though you already guard with `Array.isArray`; you can simplify this to avoid the assertion and keep runtime safety (e.g. `const conflicts = Array.isArray(preview?.conflicts) ? preview.conflicts.filter(...) : []`).

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@DavidsonGomes DavidsonGomes merged commit aa33f7d into develop Apr 26, 2026
4 checks passed
@DavidsonGomes DavidsonGomes deleted the fix/install-preview-error-surfacing branch April 26, 2026 18:08
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