Skip to content

feat(#3298): scaffold boost-backend with ProviderManager, extension point, and security middleware#3466

Open
fullsend-ai-coder[bot] wants to merge 4 commits into
mainfrom
feat/3298-boost-backend-scaffold
Open

feat(#3298): scaffold boost-backend with ProviderManager, extension point, and security middleware#3466
fullsend-ai-coder[bot] wants to merge 4 commits into
mainfrom
feat/3298-boost-backend-scaffold

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Implements the core boost-backend plugin package:

  • ProviderManager: manages registered AI providers with hot-swap support
    (registerProvider, getActiveProvider, switchProvider)
  • boostProviderExtensionPoint: extension point in boost-node for provider
    modules to register their implementations
  • boostAiProviderServiceFactory: default service factory resolving to the
    active provider via ProviderManager
  • authorizeLifecycleAction: Express middleware implementing fine-grained
    permission check -> boost.admin fallback -> 403 pattern
  • validateSecurityMode: rejects 'none' with clear error, warns when
    development-only-no-auth is used in production (NODE_ENV=production)
  • Permission registration: all 23 boost permissions registered via
    permissionsRegistry.addPermissions()
  • Resource loader placeholders for agents and tools (store integration
    deferred to later issues)

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com


Closes #3298

Post-script verification

  • Branch is not main/master (feat/3298-boost-backend-scaffold)
  • Secret scan passed (gitleaks — 3036a95068caf62a3ac06568b0d21dbc25322ac9..HEAD)
  • Pre-commit hooks passed (authoritative run on runner)
  • Tests ran inside sandbox

@fullsend-ai-coder fullsend-ai-coder Bot requested review from a team, durandom and gabemontero as code owners June 19, 2026 05:12
@rhdh-gh-app

rhdh-gh-app Bot commented Jun 19, 2026

Copy link
Copy Markdown

Missing Changesets

The following package(s) are changed by this PR but do not have a changeset:

  • @red-hat-developer-hub/backstage-plugin-boost-backend
  • @red-hat-developer-hub/backstage-plugin-boost-common
  • @red-hat-developer-hub/backstage-plugin-boost-node

See CONTRIBUTING.md for more information about how to add changesets.

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-boost-backend workspaces/boost/plugins/boost-backend none v0.1.1
@red-hat-developer-hub/backstage-plugin-boost-common workspaces/boost/plugins/boost-common none v0.1.1
@red-hat-developer-hub/backstage-plugin-boost-node workspaces/boost/plugins/boost-node none v0.1.1

@codecov

codecov Bot commented Jun 19, 2026

Copy link
Copy Markdown

⚠️ JUnit XML file not found

The CLI was unable to find any JUnit XML files to upload.
For more help, visit our troubleshooting guide.

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:14 AM UTC · Completed 5:31 AM UTC
Commit: 3036a95 · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review

Findings

Medium

  • [missing-publishConfig] workspaces/boost/plugins/boost-backend/package.json:9 — The boost-backend publishConfig only specifies "access": "public" but omits main, module, and types fields that are present in both boost-common and boost-node. Without these, npm publish would expose src/index.ts as the entry point instead of compiled dist/ output, making the published package unusable in standard Node.js environments.
    Remediation: Add "main": "dist/index.cjs.js", "module": "dist/index.esm.js", "types": "dist/index.d.ts" to publishConfig matching the sibling packages.

  • [race-condition] workspaces/boost/plugins/boost-backend/src/plugin.ts:40 — Module-level singleton ProviderManager shared between boostPlugin and boostAiProviderServiceFactory creates hidden coupling via module identity. In dynamic plugin scenarios (different bundle resolution paths in RHDH deployments), the singleton identity can break: the plugin populates one instance while the factory reads from a different one. The sibling augment plugin creates its manager inside registerInit rather than at module scope.
    Remediation: Wire the ProviderManager through the extension point or a plugin-scoped service ref rather than module closure, consistent with the augment plugin pattern.

  • [fail-open] workspaces/boost/plugins/boost-backend/src/plugin.ts:117 — The validated securityMode is computed and logged but never used to gate any behavior. No code path consults securityMode to decide whether to apply or skip the authorizeLifecycleAction middleware. The sibling augment plugin uses its security mode to gate auth policies. Currently dead code in a scaffold, but should be stored for use by routes added in later issues.
    Remediation: Store securityMode in a location accessible to route handlers (e.g., pass it to router setup or a shared context) so it can gate authorization when routes are added.

  • [fail-open] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:70 — When boost.security.mode is absent from config, validateSecurityMode defaults to development-only-no-auth. The sibling augment plugin defaults to plugin-only (the more secure option). A production deployment omitting this config key would silently operate in no-auth mode once the mode is wired up.
    Remediation: Default to plugin-only or full when config is absent, requiring operators to explicitly opt into development-only-no-auth.

  • [error-handling-idiom] workspaces/boost/plugins/boost-backend/src/provider/ProviderManager.ts:33registerProvider throws bare Error for duplicate IDs, while getActiveProvider correctly uses NotFoundError from @backstage/errors. Similarly, switchProvider throws bare Error for missing providers. The codebase convention (visible in augment, lightspeed, and other plugins) is to use typed errors: ConflictError for duplicate registration, NotFoundError for missing providers.
    Remediation: Use ConflictError in registerProvider and NotFoundError in switchProvider, both from @backstage/errors.

Low

  • [code-organization] workspaces/boost/plugins/boost-backend/src/plugin.ts:77 — The permissions service is declared as a dep (coreServices.permissions) but destructured as _permissions and never used. No other backend plugin in this repo declares a dep it immediately discards. Remove until needed.

  • [scope-creep] workspaces/boost/plugins/boost-node/src/extensions.ts — Issue boost-backend — Core plugin scaffold, ProviderManager, and security middleware (issue 2 of 15) #3298 scopes work to boost-backend, but this PR adds new exports to boost-node. This is architecturally correct (extension points belong in -node per Backstage conventions) but not explicitly authorized by the issue.

  • [authorization-design] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:150 — The admin fallback pattern means any entity with boost.admin bypasses all fine-grained permissions. This is documented and intentional, but should be explicitly noted as accepted risk — especially when conditional rules (IS_NOT_CREATOR for self-approval prevention) are added in later issues.

  • [api-contract] workspaces/boost/plugins/boost-backend/src/provider/ProviderManager.tsswitchProvider is a bare pointer swap. The AgenticProvider interface has no lifecycle methods (init/shutdown), so the implementation correctly matches the current interface. If lifecycle management is needed later, the interface will need extending.

  • [API surface - public export] workspaces/boost/plugins/boost-backend/src/provider/ProviderManager.tsProviderManager is exported as @public but the plugin's internal instance is a module-level singleton. External consumers could instantiate their own disconnected instance.

  • [pattern-inconsistency] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:38 — boost defaults to development-only-no-auth while sibling augment plugin defaults to plugin-only.

  • [intent-alignment] workspaces/boost/plugins/boost-backend/src/middleware/security.ts — Resource loaders are placeholders returning undefined. Acceptable for scaffolding but commits the public API to the ResourceLoader type signature.

  • [api-shape] workspaces/boost/plugins/boost-backend/report.api.mdResourceLoader uses an anonymous inline return type rather than a named interface.

Info

  • [backward-compatible] All changes are purely additive — no existing interfaces or types modified. No backward compatibility concerns.

  • [scope-alignment] Permission count comment says 23 boost permissions — verified accurate.

  • [scope-alignment] Health check endpoint not listed in issue tasks but follows standard practice for backend plugins.

  • [pattern-consistency] Extension point in boost-node follows the recommended Backstage pattern.

  • [module-level-state] switchProvider mutates shared state without authorization check. No HTTP route currently exposes it, but future routes must protect it with a permission check.

Previous run

Review

Reason: stale-head

The review agent reviewed commit 9bdd4577ebd7aba18802eb21eb20e575dd2b92f3 but the PR HEAD is now f4d5562f6d45643fbb4f6819d2df8aa0e335657e. This review was discarded to avoid approving unreviewed code.

Previous run (2)

Review

Findings

High

  • [logic-error] workspaces/boost/plugins/boost-backend/src/plugin.ts:55boostAiProviderServiceFactory calls providerManager.getActiveProvider() eagerly inside factory(), returning the resolved AgenticProvider at service creation time. The factory has deps: {} so it can resolve before any provider module registers via the extension point, causing getActiveProvider() to throw during startup. Additionally, switchProvider() called later has no effect on consumers who already hold the resolved value, since the factory returned the object reference once.
    Remediation: Change the factory to return a lazy proxy that delegates to providerManager.getActiveProvider() on each call, or change the service ref type to ProviderManager itself.

Medium

  • [fail-open] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:62validateSecurityMode defaults to 'development-only-no-auth' when the config key is absent. The production warning (lines 75-83) only fires when mode is explicitly set — when mode is undefined, the function returns early before the production check. So deploying to production with no config produces no warning. (Downgraded from critical because the security mode is not yet enforced by any route.)
    Remediation: Move the production warning check to fire after the default is applied, so the warning triggers whenever the effective mode is 'development-only-no-auth' regardless of how it was selected.

  • [security-mode-not-enforced] workspaces/boost/plugins/boost-backend/src/plugin.ts:103 — The securityMode value is validated and logged but never used to gate any behavior. No routes use authorizeLifecycleAction, and all three modes produce identical runtime behavior.
    Remediation: Wire the security mode into plugin initialization when routes are added, or document that security mode enforcement is not yet active.

  • [api-contract] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:144authorizeLifecycleAction accepts BasicPermission but many boost permissions (agent promote/approve/demote/publish/unpublish/withdraw/delete, all tool permissions) are ResourcePermission. Callers needing to guard lifecycle actions on resource-scoped permissions will get TypeScript errors.
    Remediation: Widen the parameter type to accept Permission from @backstage/plugin-permission-common, or add a separate function for resource-scoped checks.

  • [module-level-singleton] workspaces/boost/plugins/boost-backend/src/plugin.ts:39ProviderManager is instantiated as a module-level singleton shared between plugin registration and service factory. This bypasses Backstage's DI system and creates test isolation concerns (state leaks between tests if the module is imported in multiple test files).
    Remediation: Manage ProviderManager through Backstage's service system, or add a reset() method for test cleanup.

  • [test-inadequate] workspaces/boost/plugins/boost-backend/src/middleware/security.test.ts:259 — No test covers the production warning scenario for the default case (mode=undefined in NODE_ENV=production). This test gap directly corresponds to the missing-warning bug identified above.
    Remediation: Add test: validateSecurityMode(undefined, logger) with NODE_ENV=production should trigger warning.

  • [publishConfig-consistency] workspaces/boost/plugins/boost-backend/package.json:8publishConfig only has "access": "public" but sibling packages boost-common and boost-node include main/module/types pointing to dist/. When published, consumers would try to resolve from src/index.ts which won't exist in the tarball since files only includes dist.
    Remediation: Add publishConfig entries for main and types pointing to dist output, matching sibling packages.

Low

  • [api-contract] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:118_resourceLoader parameter is accepted but unused. Exports placeholder API (ResourceLoader, createAgentResourceLoader, createToolResourceLoader) for no current benefit.

  • [authorization-bypass] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:155 — The admin fallback pattern means any user with boost.admin can bypass all fine-grained permissions. Documented as intentional design.

  • [error-handling-idiom] workspaces/boost/plugins/boost-backend/src/provider/ProviderManager.ts:33ProviderManager throws bare Error instead of typed errors from @backstage/errors (NotFoundError, ConflictError).

  • [unused-dependency] workspaces/boost/plugins/boost-backend/src/plugin.ts:105httpAuth declared in deps but not destructured or used in init.

  • [script-consistency] workspaces/boost/plugins/boost-backend/package.json:59 — Scripts block has both lint and lint:check pointing to the same command.

Info

  • [placeholder-resource-loaders] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:186 — Resource loaders are placeholders returning undefined. Conditional authorization cannot be evaluated until implemented. Tracked as security debt.
Previous run (3)

Review

Reason: stale-head

The review agent reviewed commit f8acc23586e30759f94325a2f996ef45a0aaf1ca but the PR HEAD is now 8c8efe512e68a4afd6f383942988994f43317b94. This review was discarded to avoid approving unreviewed code.

Previous run (4)

Review

Findings

High

  • [fail-open] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:79validateSecurityMode defaults to 'development-only-no-auth' when no configuration is provided (mode is undefined). A deployment that omits the boost.security.mode config key silently runs with authentication/authorization disabled. A fail-closed design would default to the most restrictive mode ('full') or require the key to be explicitly set.
    Remediation: Change the default from 'development-only-no-auth' to 'full', or require the config key to be explicitly set and throw an error when it is absent.

  • [logic-error] workspaces/boost/plugins/boost-backend/src/plugin.ts:48 — The service factory's factory() function eagerly calls providerManager.getActiveProvider(), which throws if no provider has been registered. Since createServiceFactory evaluates factory() when a consumer resolves the service ref and returns the AgenticProvider value directly (not a lazy accessor), two problems arise: (1) if no provider module is installed, backend startup crashes; (2) if switchProvider() is called later, consumers holding the original reference will not see the updated provider.
    Remediation: Change the factory to return a proxy object or a manager reference that delegates to providerManager.getActiveProvider() at call time, or restructure so the service factory is created inside the plugin's registerInit after providers have registered.

Medium

  • [authorization-bypass] workspaces/boost/plugins/boost-backend/src/plugin.ts:97 — The validated securityMode value is stored in a local variable and logged but never passed to any middleware or route guard. The authorizeLifecycleAction middleware has no parameter or branch for security mode. The mode is effectively dead code that creates a false sense of configurability. Setting boost.security.mode to 'full' has no observable effect.
    Remediation: Thread the securityMode into the authorization middleware, or defer the security mode infrastructure until it can be wired into route guards.

Low

  • [api-contract] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:108authorizeLifecycleAction accepts only BasicPermission, but most lifecycle permissions in boost-common are ResourcePermission types. The function name implies it handles all lifecycle transitions but structurally cannot. The JSDoc documents this limitation but the naming mismatch remains.

  • [authorization-bypass] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:142 — The admin fallback pattern means any user with boost.admin can bypass all fine-grained permissions. This is an intentional documented design trade-off, but worth noting for future conditional authorization work.

  • [missing-auth-gate] workspaces/boost/plugins/boost-backend/src/plugin.ts:130 — Only /health has an explicit auth policy. Backstage defaults are secure (require authentication), but an explicit default policy would make the intent clearer.

  • [unused-dependency] workspaces/boost/plugins/boost-backend/src/plugin.ts:79httpAuth is declared in deps but never destructured in init. permissions is destructured as _permissions (unused).

  • [test-inadequate] workspaces/boost/plugins/boost-backend/src/middleware/security.test.tsauthorizeLifecycleAction tests do not cover the error path where httpAuth.credentials() throws, and do not verify that the correct permission object is passed to permissions.authorize().

  • [architectural-coherence] workspaces/boost/plugins/boost-backend/src/provider/ProviderManager.ts:34ProviderManager uses a raw Map for its provider registry. AGENTS.md prohibits raw Map<> caches. This is a registry (not a cache), so the distinction is valid, but a clarifying comment would help.

  • [error-handling-idiom] workspaces/boost/plugins/boost-backend/src/provider/ProviderManager.ts:57getActiveProvider() throws bare Error. The codebase uses Backstage error classes (NotFoundError) from @backstage/errors for domain-specific errors.

Info

  • [scope-coherence] workspaces/boost/plugins/boost-backend/src/plugin.ts:60 — JSDoc comment hardcodes "all 23 boost permissions". The count is currently accurate and the runtime log uses boostPermissions.length dynamically.

  • [scope-alignment] workspaces/boost/plugins/boost-backend/package.json:15 — New package starts at version 0.1.1 rather than 0.1.0. Consistent with peer boost packages.

Previous run (5)

Review

Findings

High

  • [logic-error] workspaces/boost/plugins/boost-backend/src/plugin.ts:47 — The boostAiProviderServiceFactory calls providerManager.getActiveProvider() eagerly inside factory(), which executes once at service-resolution time. If no provider module has registered yet when the factory resolves, getActiveProvider() throws 'No AI provider is registered'. Even if initialization order happens to work, the factory returns a fixed reference — subsequent switchProvider() calls won't affect consumers that already resolved the service.
    Remediation: Change the factory to return a lazy wrapper/proxy that delegates chat, chatStream, and descriptor to providerManager.getActiveProvider() at invocation time rather than resolution time.

Medium

  • [fail-open] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:63 — When boost.security.mode is absent from configuration, validateSecurityMode defaults to 'development-only-no-auth'. The production warning relies on NODE_ENV=production which may not be set in all production-like deployments. While the security mode is currently validated and logged but not yet consumed by downstream logic (making this a latent design concern rather than an active vulnerability), defaulting to the least restrictive mode is a fail-open pattern that should be corrected before the mode is wired up.
    Remediation: Default to the most restrictive mode ('full') when no mode is configured, or at minimum require explicit opt-in for development-only-no-auth.

  • [api-contract] workspaces/boost/plugins/boost-backend/src/plugin.ts:29 — Module-level singleton const providerManager = new ProviderManager() couples the factory to the plugin module's import graph. If imported from different module instances (e.g., different package versions in the dependency tree), they'd operate on different ProviderManager instances. Deviates from Backstage DI patterns where state is created inside register().
    Remediation: Move new ProviderManager() inside the register() function body and wire through Backstage's DI (e.g., an internal serviceRef).

Low

  • [security-mode-unused] workspaces/boost/plugins/boost-backend/src/plugin.ts:127 — The validated securityMode value is computed and logged but never consumed by any downstream logic. Expected for a scaffold PR but worth tracking.

  • [auth-bypass] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:163 — The authorizeLifecycleAction middleware falls back to boost.admin when a fine-grained permission is denied. While documented as intentional, this means admin holders implicitly bypass all fine-grained permissions, including future separation-of-duties controls.

  • [resource-loader-placeholder] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:195 — Both createAgentResourceLoader and createToolResourceLoader always return undefined, and _resourceLoader is never called. Documented as placeholder behavior for the scaffold.

  • [test-inadequate] workspaces/boost/plugins/boost-backend/src/middleware/security.test.ts:241 — Missing tests for httpAuth.credentials() rejection and permissions.authorize() errors being forwarded to next().

  • [unused-dependency] workspaces/boost/plugins/boost-backend/src/plugin.ts:79httpAuth declared in deps but not destructured; permissions destructured as _permissions but unused.

  • [race-condition] workspaces/boost/plugins/boost-backend/src/provider/ProviderManager.ts:39 — No synchronization in ProviderManager, though safe in Node.js single-threaded synchronous context.

  • [error-handling-pattern] workspaces/boost/plugins/boost-backend/src/provider/ProviderManager.ts:57 — Uses plain Error instead of @backstage/errors classes (NotFoundError, ConflictError), unlike security.ts which uses NotAllowedError.

  • [export-style] workspaces/boost/plugins/boost-backend/src/index.ts:23 — Exports more items than typical for backend plugins in this repo. Exporting ProviderManager as a public class creates a larger contract surface.

  • [type-safety] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:17authorizeLifecycleAction accepts BasicPermission but some boost permissions are ResourcePermission. TypeScript prevents misuse at compile time, but the gap between available permissions and middleware capability is notable.

Info

  • [unauthenticated-endpoint] workspaces/boost/plugins/boost-backend/src/plugin.ts:131/health endpoint is unauthenticated with static response. Standard Backstage pattern.

  • [silent-default] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:42 — Defaults to dev mode without logging the default case. The caller does log the resulting mode.

  • [permission-registration] workspaces/boost/plugins/boost-backend/src/plugin.ts:100 — All 23 boost permissions registered with the Backstage permission framework.

  • [backward-compatibility] workspaces/boost/plugins/boost-node/src/index.ts:24 — boost-node gains two new additive exports. No existing exports changed. Backward compatible.

Previous run (6)

Review

Findings

Critical

  • [logic-error] workspaces/boost/plugins/boost-backend/src/plugin.ts:48 — The service factory's factory() method calls providerManager.getActiveProvider() eagerly at factory creation time and returns the result directly. This will throw 'No AI provider is registered' if no provider module has registered yet (extension point registration order is not guaranteed relative to service factory resolution). Additionally, the factory returns the AgenticProvider object directly — not a lazy proxy — so runtime provider switching via switchProvider() will not be reflected; consumers hold a stale reference.
    Remediation: Return a lazy proxy that delegates to providerManager.getActiveProvider() on each call, or return the ProviderManager itself and update the service ref type accordingly.

Medium

  • [api-contract] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:109authorizeLifecycleAction accepts BasicPermission but several boost-common permissions are ResourcePermission types. The TypeScript constraint prevents passing resource-scoped permissions. When ResourcePermission support is added, widening the parameter type will be a public API change.
    Remediation: Widen the parameter type to accept Permission now, or add a runtime check that rejects resource-scoped permissions with a clear error.

  • [fail-open] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:74validateSecurityMode() defaults to 'development-only-no-auth' when no config is provided — a fail-open default. While the mode is currently not wired to any auth gate (auth always runs), the default creates risk for when the mode is eventually consumed. The production warning is advisory-only.
    Remediation: Change the default to 'full' (fail-closed). Require explicit config for dev-no-auth and refuse to start in production unless mode is 'plugin-only' or 'full'.

  • [scope-alignment] workspaces/boost/plugins/boost-backend/src/plugin.ts:93securityMode is computed from config and logged but never consumed by any middleware or auth gate. The value is dead code in the current implementation.
    Remediation: Document that mode wiring is deferred, or enforce: throw error (not warn) when NODE_ENV=production with 'development-only-no-auth'.

  • [permission-expansion] workspaces/boost/plugins/boost-backend/src/plugin.ts:100 — Plugin registers 23 permissions via permissionsRegistry.addPermissions() and createPermissionIntegrationRouter but does not register any permission rules or resource types. Without rules, conditional policies for the 12 resource-scoped permissions cannot be evaluated.
    Remediation: Add a TODO comment noting that rules and resourcePermissions must be registered. Confirm the framework fails closed when rules are missing.

  • [architectural-conflict] workspaces/boost/plugins/boost-backend/src/plugin.ts:29 — Module-level singleton ProviderManager is instantiated outside any Backstage lifecycle. Cannot be reset between tests, couples service factory to module scope.
    Remediation: Consider moving instantiation inside register() and wiring through Backstage DI mechanisms.

  • [error-handling-idiom] workspaces/boost/plugins/boost-backend/src/provider/ProviderManager.ts:57getActiveProvider() throws plain Error instead of @backstage/errors types. The package already depends on @backstage/errors. Using plain Error means Backstage error middleware cannot map to a proper HTTP status.
    Remediation: Use NotFoundError or ServiceUnavailableError from @backstage/errors.

Low

  • [error-handling] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:115 — Destructuring const [decision] = await permissions.authorize(...) will produce undefined if the service returns an empty array. A defensive guard would produce a clearer error message.
  • [test-inadequate] workspaces/boost/plugins/boost-backend/src/middleware/security.test.ts:240authorizeLifecycleAction tests don't cover error paths (httpAuth/permissions throwing).
  • [test-inadequate] workspaces/boost/plugins/boost-backend/src/middleware/security.test.ts:170 — No test for empty string security mode case.
  • [edge-case] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:82SecurityMode type and VALID_SECURITY_MODES array are manually synced. Use as const derivation for compile-time safety.
  • [scope-creep] workspaces/boost/plugins/boost-backend/src/provider/ProviderManager.ts:71switchProvider method is not authorized by issue boost-backend — Core plugin scaffold, ProviderManager, and security middleware (issue 2 of 15) #3298.
  • [scope-alignment] workspaces/boost/plugins/boost-backend/src/plugin.ts:87permissions service injected but unused (_permissions).
  • [api-completeness] workspaces/boost/plugins/boost-backend/src/index.ts:27ProviderManager exported as concrete class exposes switchProvider() to all consumers; consider a narrower interface.
  • [api-consistency] workspaces/boost/plugins/boost-backend/src/index.ts:29 — Placeholder resource loaders exported as public API; consider @alpha/@beta tags.
  • [pattern-inconsistency] workspaces/boost/plugins/boost-backend/package.json:68 — Test script flags (--passWithNoTests --coverage) differ from siblings.
  • [pattern-inconsistency] workspaces/boost/plugins/boost-backend/package.json:65 — Extra scripts (lint:check, lint:fix, tsc, start) absent in siblings.
  • [pattern-inconsistency] workspaces/boost/plugins/boost-backend/package.json:38exports/typesVersions fields absent in siblings.
  • [architectural-conflict] workspaces/boost/plugins/boost-backend/src/provider/ProviderManager.ts:34 — Raw Map usage defensible (non-serializable instances); add explanatory comment re: AGENTS.md cache rule.
  • [api-design] workspaces/boost/plugins/boost-backend/src/middleware/security.ts:111ResourceLoader return type not backed by shared type in boost-common; formalize when loaders are implemented.

@fullsend-ai-review fullsend-ai-review Bot 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.

See the review comment for full details.

Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
Comment thread workspaces/boost/plugins/boost-backend/src/middleware/security.ts
Comment thread workspaces/boost/plugins/boost-backend/src/middleware/security.ts
Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
Comment thread workspaces/boost/plugins/boost-backend/package.json
Comment thread workspaces/boost/plugins/boost-backend/package.json
Comment thread workspaces/boost/plugins/boost-backend/package.json
Comment thread workspaces/boost/plugins/boost-backend/src/middleware/security.ts
@gabemontero gabemontero force-pushed the feat/3298-boost-backend-scaffold branch from 5eaeee2 to be2f52a Compare June 19, 2026 13:38
@rhdh-qodo-merge

Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: Workspace boost, CI step for node 24

Failed stage: check for missing repo fixes [❌]

Failed test name: ""

Failure summary:

The action failed during the yarn fix --check step because Yarn detected the repo is not in the
expected “fixed/synchronized” state.
- yarn fix --check reported: “The following packages are out of
sync, run yarn fix to fix them: @red-hat-developer-hub/backstage-plugin-boost-backend” (lines
316-318).
- This caused the command to exit with code 1, which failed the GitHub Action (line 318).

- Earlier Yarn warnings about unmet peer dependencies (e.g., missing webpack, prettier version
mismatch, missing @typescript-eslint/parser) were warnings and did not directly stop the install,
but may be related to the package being considered “out of sync”.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

251:  �[93m➤�[39m YN0002: │ �[38;5;166m@red-hat-developer-hub/�[39m�[38;5;173mbackstage-plugin-boost-backend�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:plugins/boost-backend�[39m doesn't provide �[38;5;173mwebpack�[39m (�[38;5;111mp405fc5�[39m), requested by �[38;5;166m@backstage/�[39m�[38;5;173mcli�[39m.
252:  �[93m➤�[39m YN0002: │ �[38;5;166m@red-hat-developer-hub/�[39m�[38;5;173mbackstage-plugin-boost-common�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:plugins/boost-common [c2cab]�[39m doesn't provide �[38;5;173mwebpack�[39m (�[38;5;111mp8e1e5a�[39m), requested by �[38;5;166m@backstage/�[39m�[38;5;173mcli�[39m.
253:  �[93m➤�[39m YN0002: │ �[38;5;166m@red-hat-developer-hub/�[39m�[38;5;173mbackstage-plugin-boost-common�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:plugins/boost-common�[39m doesn't provide �[38;5;173mwebpack�[39m (�[38;5;111mpe50ee4�[39m), requested by �[38;5;166m@backstage/�[39m�[38;5;173mcli�[39m.
254:  �[93m➤�[39m YN0002: │ �[38;5;166m@red-hat-developer-hub/�[39m�[38;5;173mbackstage-plugin-boost-node�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:plugins/boost-node�[39m doesn't provide �[38;5;173mwebpack�[39m (�[38;5;111mp456cbf�[39m), requested by �[38;5;166m@backstage/�[39m�[38;5;173mcli�[39m.
255:  �[93m➤�[39m YN0086: │ Some peer dependencies are incorrectly met by your project; run �[38;5;111myarn explain peer-requirements <hash>�[39m for details, where �[38;5;111m<hash>�[39m is the six-letter p-prefixed code.
256:  �[93m➤�[39m YN0086: │ Some peer dependencies are incorrectly met by dependencies; run �[38;5;111myarn explain peer-requirements�[39m for details.
257:  ##[endgroup]
258:  �[94m➤�[39m �[90mYN0000�[39m: └ Completed
259:  �[94m➤�[39m �[90mYN0000�[39m: ┌ Fetch step
260:  ##[group]Fetch step
261:  �[94m➤�[39m YN0013: │ �[38;5;220m1674�[39m packages were added to the project (�[38;5;160m+ 516.03 MiB�[39m).
262:  ##[endgroup]
263:  �[94m➤�[39m �[90mYN0000�[39m: └ Completed in 7s 399ms
264:  �[94m➤�[39m �[90mYN0000�[39m: ┌ Link step
265:  ##[group]Link step
266:  �[94m➤�[39m YN0007: │ �[38;5;166m@fission-ai/�[39m�[38;5;173mopenspec�[39m�[38;5;111m@�[39m�[38;5;111mnpm:1.4.1�[39m must be built because it never has been before or the last one failed
267:  �[94m➤�[39m YN0007: │ �[38;5;166m@swc/�[39m�[38;5;173mcore�[39m�[38;5;111m@�[39m�[38;5;111mnpm:1.15.40 [486b9]�[39m must be built because it never has been before or the last one failed
268:  �[94m➤�[39m YN0007: │ �[38;5;173mesbuild�[39m�[38;5;111m@�[39m�[38;5;111mnpm:0.25.12�[39m must be built because it never has been before or the last one failed
269:  �[94m➤�[39m YN0007: │ �[38;5;166m@nestjs/�[39m�[38;5;173mcore�[39m�[38;5;111m@�[39m�[38;5;111mnpm:11.1.21 [26b97]�[39m must be built because it never has been before or the last one failed
270:  �[94m➤�[39m YN0007: │ �[38;5;166m@openapitools/�[39m�[38;5;173mopenapi-generator-cli�[39m�[38;5;111m@�[39m�[38;5;111mnpm:2.34.0�[39m must be built because it never has been before or the last one failed
271:  �[94m➤�[39m YN0007: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m must be built because it never has been before or the last one failed
272:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[93m➤�[39m YN0000: Yarn detected that the current workflow is executed from a public pull request. For safety the hardened mode has been enabled.
...

280:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[94m➤�[39m �[90mYN0000�[39m: ┌ Post-resolution validation
281:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m ::group::Post-resolution validation
282:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[93m➤�[39m YN0060: │ �[38;5;173mprettier�[39m is listed by your project with version �[38;5;111m3.7.4�[39m (�[38;5;111mpc2ecd8�[39m), which doesn't satisfy what �[38;5;166m@spotify/�[39m�[38;5;173mprettier-config�[39m and other dependencies request (�[38;5;37m^2.0.0�[39m).
283:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[93m➤�[39m YN0002: │ �[38;5;166m@redhat-developer/�[39m�[38;5;173mrhdh-plugins�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m doesn't provide �[38;5;166m@typescript-eslint/�[39m�[38;5;173mparser�[39m (�[38;5;111mp8d7c5c�[39m), requested by �[38;5;166m@spotify/�[39m�[38;5;173meslint-plugin�[39m.
284:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[93m➤�[39m YN0086: │ Some peer dependencies are incorrectly met by your project; run �[38;5;111myarn explain peer-requirements <hash>�[39m for details, where �[38;5;111m<hash>�[39m is the six-letter p-prefixed code.
285:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[93m➤�[39m YN0086: │ Some peer dependencies are incorrectly met by dependencies; run �[38;5;111myarn explain peer-requirements�[39m for details.
286:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m ::endgroup::
287:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[94m➤�[39m �[90mYN0000�[39m: └ Completed
288:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[94m➤�[39m �[90mYN0000�[39m: ┌ Fetch step
289:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m ::group::Fetch step
290:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[94m➤�[39m YN0013: │ �[38;5;220m690�[39m packages were added to the project (�[38;5;160m+ 288.05 MiB�[39m).
291:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m ::endgroup::
292:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[94m➤�[39m �[90mYN0000�[39m: └ Completed in 4s 365ms
293:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[94m➤�[39m �[90mYN0000�[39m: ┌ Link step
294:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m ::group::Link step
295:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[94m➤�[39m YN0007: │ �[38;5;173mesbuild�[39m�[38;5;111m@�[39m�[38;5;111mnpm:0.21.5�[39m must be built because it never has been before or the last one failed
296:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[94m➤�[39m YN0007: │ �[38;5;166m@swc/�[39m�[38;5;173mcore�[39m�[38;5;111m@�[39m�[38;5;111mnpm:1.4.13 [366d3]�[39m must be built because it never has been before or the last one failed
297:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[94m➤�[39m YN0007: │ �[38;5;173mesbuild�[39m�[38;5;111m@�[39m�[38;5;111mnpm:0.23.1�[39m must be built because it never has been before or the last one failed
298:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[94m➤�[39m YN0007: │ �[38;5;173mesbuild�[39m�[38;5;111m@�[39m�[38;5;111mnpm:0.20.2�[39m must be built because it never has been before or the last one failed
299:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[94m➤�[39m YN0007: │ �[38;5;173mcore-js-pure�[39m�[38;5;111m@�[39m�[38;5;111mnpm:3.36.1�[39m must be built because it never has been before or the last one failed
300:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[94m➤�[39m YN0007: │ �[38;5;166m@redhat-developer/�[39m�[38;5;173mrhdh-plugins�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m must be built because it never has been before or the last one failed
301:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m ::endgroup::
...

303:  �[94m➤�[39m �[90mYN0000�[39m: │ �[38;5;166m@internal/�[39m�[38;5;173mboost�[39m�[38;5;111m@�[39m�[38;5;111mworkspace:.�[39m �[32mSTDOUT�[39m �[93m➤�[39m YN0000: · Done with warnings in 19s 47ms
304:  ##[endgroup]
305:  �[94m➤�[39m �[90mYN0000�[39m: └ Completed in 29s 631ms
306:  �[93m➤�[39m YN0000: · Done with warnings in 45s 580ms
307:  ##[group]Run yarn fix --check
308:  �[36;1myarn fix --check�[0m
309:  shell: /usr/bin/bash -e {0}
310:  env:
311:  CI: true
312:  NODE_OPTIONS: --max-old-space-size=8192
313:  NPM_CONFIG_USERCONFIG: /home/runner/work/_temp/.npmrc
314:  NODE_AUTH_TOKEN: XXXXX-XXXXX-XXXXX-XXXXX
315:  ##[endgroup]
316:  The following packages are out of sync, run 'yarn fix' to fix them:
317:  @red-hat-developer-hub/backstage-plugin-boost-backend
318:  ##[error]Process completed with exit code 1.
319:  Node 20 is being deprecated. This workflow is running with Node 24 by default. If you need to temporarily use Node 20, you can set the ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true environment variable. For more information see: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 19, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:40 PM UTC · Completed 1:55 PM UTC
Commit: 7ccaff1 · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot 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.

See the review comment for full details.

Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
Comment thread workspaces/boost/plugins/boost-backend/src/middleware/security.ts
Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
Comment thread workspaces/boost/plugins/boost-backend/src/middleware/security.ts
Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
Comment thread workspaces/boost/plugins/boost-backend/src/index.ts
Comment thread workspaces/boost/plugins/boost-backend/src/middleware/security.ts
@gabemontero gabemontero force-pushed the feat/3298-boost-backend-scaffold branch from be2f52a to fc922ee Compare June 20, 2026 15:13
@fullsend-ai-review

fullsend-ai-review Bot commented Jun 20, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:17 PM UTC · Completed 3:32 PM UTC
Commit: c3c2966 · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot 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.

See the review comment for full details.

Comment thread workspaces/boost/plugins/boost-backend/src/middleware/security.ts
Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
Comment thread workspaces/boost/plugins/boost-backend/src/middleware/security.ts
Comment thread workspaces/boost/plugins/boost-backend/src/middleware/security.ts
Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
@fullsend-ai-review

fullsend-ai-review Bot commented Jun 21, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:54 PM UTC · Completed 3:07 PM UTC
Commit: f2b9c97 · View workflow run →

@gabemontero gabemontero force-pushed the feat/3298-boost-backend-scaffold branch from f8acc23 to c3d4638 Compare June 21, 2026 14:57
@fullsend-ai-review

Copy link
Copy Markdown

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 21, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 3:09 PM UTC · Completed 3:24 PM UTC
Commit: f2b9c97 · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot 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.

See the review comment for full details.

Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
Comment thread workspaces/boost/plugins/boost-backend/src/middleware/security.ts
Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
Comment thread workspaces/boost/plugins/boost-backend/src/middleware/security.ts
Comment thread workspaces/boost/plugins/boost-backend/src/plugin.ts
Comment thread workspaces/boost/plugins/boost-backend/package.json
fullsend-ai-coder Bot and others added 3 commits June 21, 2026 14:36
…oint, and security middleware

Implements the core boost-backend plugin package:

- ProviderManager: manages registered AI providers with hot-swap support
  (registerProvider, getActiveProvider, switchProvider)
- boostProviderExtensionPoint: extension point in boost-node for provider
  modules to register their implementations
- boostAiProviderServiceFactory: default service factory resolving to the
  active provider via ProviderManager
- authorizeLifecycleAction: Express middleware implementing fine-grained
  permission check -> boost.admin fallback -> 403 pattern
- validateSecurityMode: rejects 'none' with clear error, warns when
  development-only-no-auth is used in production (NODE_ENV=production)
- Permission registration: all 23 boost permissions registered via
  permissionsRegistry.addPermissions()
- Resource loader placeholders for agents and tools (store integration
  deferred to later issues)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Removes stale publishConfig entries (main, module, types) that
backstage-cli repo fix strips for backend-plugin roles. Fixes the
`yarn fix --check` CI gate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add missing report.api.md for boost-backend (new package)
- Update boost-node report.api.md with extension point exports
- Deduplicate 5 lockfile entries (catalog-client, permission-node,
  backend-plugin-api, auth-node)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gabemontero gabemontero force-pushed the feat/3298-boost-backend-scaffold branch from 8c8efe5 to 9bdd457 Compare June 21, 2026 18:36
- Service factory now returns a lazy proxy that delegates chat,
  chatStream, and descriptor to providerManager.getActiveProvider()
  at invocation time, not resolution time. This fixes a race where
  provider modules register after the factory is resolved.

- ProviderManager.getActiveProvider() now throws NotFoundError from
  @backstage/errors instead of plain Error, giving Backstage's error
  middleware a proper HTTP 404 status.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: gabemontero <gmontero@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Jun 21, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:38 PM UTC · Completed 6:54 PM UTC
Commit: 6e3057c · View workflow run →

@sonarqubecloud

Copy link
Copy Markdown

@fullsend-ai-review

Copy link
Copy Markdown

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 21, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 6:57 PM UTC · Completed 7:13 PM UTC
Commit: 6e3057c · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot 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.

See the review comment for full details.

"main": "src/index.ts",
"types": "src/index.ts",
"publishConfig": {
"access": "public"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[medium] missing-publishConfig

The boost-backend publishConfig only specifies access:public but omits main, module, types fields present in sibling packages. Without these, npm publish would expose src/index.ts instead of compiled dist/ output, making the published package unusable.

Suggested fix: Add main, module, and types fields to publishConfig matching the pattern used by boost-common and boost-node.

*
* @internal
*/
const providerManager = new ProviderManager();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[medium] race-condition

Module-level singleton ProviderManager shared between boostPlugin and boostAiProviderServiceFactory creates hidden coupling via module identity. In dynamic plugin scenarios the singleton identity can break. The augment plugin creates its manager inside registerInit instead.

Suggested fix: Wire ProviderManager through a plugin-scoped service ref or extension point instead of module closure.

logger.info('Initializing boost backend plugin');

// Validate security mode (rejects 'none', warns in production)
const securityMode = validateSecurityMode(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[medium] fail-open

The validated securityMode is computed and logged but never used to gate any behavior. No code path consults it to decide whether to apply or skip authorization middleware.

Suggested fix: Store securityMode in a location accessible to route handlers so it can gate authorization when routes are added.

'Security mode "none" is not a valid mode name. ' +
'Use "development-only-no-auth" instead. ' +
`Valid modes: ${VALID_SECURITY_MODES.join(', ')}`,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[medium] fail-open

When boost.security.mode is absent from config, validateSecurityMode defaults to development-only-no-auth. The sibling augment plugin defaults to the more secure plugin-only. A production deployment omitting this key would silently run without auth once the mode is wired up.

Suggested fix: Default to plugin-only or full when config is absent, requiring explicit opt-in to development-only-no-auth.

*/
export class ProviderManager {
private activeProvider: AgenticProvider | undefined;
private readonly providers = new Map<string, AgenticProvider>();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[medium] error-handling-idiom

registerProvider throws bare Error for duplicate IDs while getActiveProvider correctly uses NotFoundError. switchProvider also throws bare Error. The codebase convention is typed errors: ConflictError for duplicates, NotFoundError for missing providers.

Suggested fix: Use ConflictError in registerProvider and NotFoundError in switchProvider, both from @backstage/errors.

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.

boost-backend — Core plugin scaffold, ProviderManager, and security middleware (issue 2 of 15)

1 participant