diff --git a/workspaces/boost/specifications/JIRA-analysis/RHDHPLAN-1507-feasibility-report.md b/workspaces/boost/specifications/JIRA-analysis/RHDHPLAN-1507-feasibility-report.md new file mode 100644 index 0000000000..0744abe11e --- /dev/null +++ b/workspaces/boost/specifications/JIRA-analysis/RHDHPLAN-1507-feasibility-report.md @@ -0,0 +1,251 @@ +# RHDHPLAN-1507 Feasibility Report: Entity Model & Ingestion Criteria vs. Backstage Catalog Framework + +**Date:** 2026-07-07 +**Purpose:** Assess whether each RHIDP epic under RHDHPLAN-1507 can be implemented within the existing Backstage catalog framework without requiring upstream changes. + +--- + +## Framework Capabilities Summary + +RHDHPLAN-1507 focuses on entity model, ingestion infrastructure, and data pipelines. The cross-reference target is the Backstage Software Catalog rather than the permission framework. + +| Capability | Status | +| --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| Custom `spec.type` values (free-form string) | **Available** — any value accepted, low risk | +| Custom annotations (`metadata.annotations`) | **Available** — no risk if properly namespaced (e.g., `rhdh.io/`) | +| Custom labels | **Available** — same namespacing rules as annotations | +| Custom relations emitted by processors | **Available** — any processor can emit new relation types | +| Built-in entity kinds: Component, Resource, API, System, Domain, Group, User, Template, Location | **Available** | +| Custom entity kinds (new `apiVersion`/`kind` combinations) | **Available but high-impact** — requires custom processor, validation, many plugins hard-code kind checks | +| `EntityProvider` interface (`connect`, `applyMutation`) | **Available** — full provider lifecycle | +| Full mutation (`type: 'full'`) — replaces entire bucket | **Available** — efficient delta under the hood | +| Delta mutation (`type: 'delta'`) — explicit upserts/deletions | **Available** — event-driven pattern | +| `IncrementalEntityProvider` — paginated ingestion with cursor | **Available** — `next()` + `around()` + `done` flag | +| `locationKey` conflict resolution | **Available** — prevents rogue entity takeovers | +| Custom processors (`CatalogProcessor`) for validation and relation emission | **Available** — via `catalogProcessingExtensionPoint` | +| `catalogModelExtensionPoint` for field validators | **Available** | +| Scheduled task runners via `SchedulerService` | **Available** — cron or interval-based | +| App-config-driven provider schedule | **Available** — `catalog.providers..schedule` | +| Custom spec fields on existing kinds | **Available** — schema validation doesn't forbid unknown fields | +| Processing pipeline (validation → enrichment → relation emission → stitching) | **Available** — entities go through processors after provider emission | +| Per-provider isolated entity buckets | **Available** — each provider instance owns its bucket | +| Required annotations: `backstage.io/managed-by-location`, `backstage.io/managed-by-origin-location` | **Required** — entities without these won't appear | +| Secondary data stores (Neo4j, etc.) as derived indexes | **Not built-in** — must be custom-built; no catalog extension point for secondary sync | +| OCI registry integration | **Not built-in** — must be custom-built; no catalog-native OCI support | +| Air-gapped CA bundle / credential injection | **Not built-in at catalog level** — standard Node.js/K8s patterns apply | + +--- + +## Epic-by-Epic Analysis + +### RHIDP-15254: AI Asset Annotation Scheme (`rhdh.io/ai-asset-*`) + +**Summary:** Define and implement `rhdh.io/ai-asset-category`, `rhdh.io/ai-asset-version`, and `rhdh.io/ai-asset-source` annotations on all AI asset catalog entities. Cover all five `spec.type` values mapped to Backstage entity kinds. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| All entities include `rhdh.io/ai-asset-category` annotation with one of: `agent`, `skill`, `mcp-server`, `ai-model`, `model-server` | **YES** | Custom annotations are fully supported. The `rhdh.io/` prefix is a valid custom namespace. Entity providers set annotations at emission time | +| All entities include `rhdh.io/ai-asset-version` with normalization scheme | **YES** | Same — custom annotation, set by entity provider during ingestion | +| All entities include `rhdh.io/ai-asset-source` identifying connector/registry | **YES** | Same — custom annotation | +| Missing annotations rejected by validation layer with descriptive error | **YES** | A custom `CatalogProcessor` with `validateEntityKind()` can reject entities missing required annotations. Alternatively, validation in the entity provider itself before `applyMutation` | +| Category-to-entity-kind mapping documented: `ai-agent` (Resource), `ai-skill` (Resource), `mcp-server` (Resource), `ai-model` (Resource), `model-server` (Component) | **YES** | All mapped to built-in kinds (Resource, Component). `spec.type` is free-form — any value is accepted by the catalog. No new kinds needed | +| `rhdh.io/ai-asset-version` annotation designed for migration-readiness toward upstream entity kinds (RFCs #32062, #33060) | **YES** | Migration-readiness is a design exercise — the annotation scheme documents how to transform entities to future upstream kinds. No framework changes needed for the scheme itself | +| Migration-readiness design reviewed/signed off | **YES** | Process requirement — not a framework concern | +| Annotation scheme documented in reference page | **YES** | Documentation exercise | + +**Verdict: FULLY FEASIBLE** — Custom annotations, custom `spec.type` values, and mapping to existing entity kinds (Resource, Component) are all standard Backstage catalog extension patterns. The framework explicitly supports and encourages this. No upstream changes needed. + +**Key strength:** By mapping to existing kinds (Resource, Component) rather than introducing custom kinds, this avoids the "high-impact" risk of custom kinds where many plugins hard-code kind checks. + +--- + +### RHIDP-15258: Entity-Provider SDK Package with Typed Contract + +**Summary:** Publish a versioned SDK package with TypeScript interfaces, shared validation, `skillcard.yaml` validation, Neo4j sync adapter interface, and documented breaking-change policy. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| --------------------------------------------------------------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Published npm package with TypeScript interface(s) defining entity-provider contract | **YES** | Standard npm package publishing. The interfaces wrap the Backstage `EntityProvider` contract with AI Catalog-specific types. No framework changes needed | +| Shared validation for `rhdh.io/ai-asset-*` annotations and kind/type mapping | **YES** | Validation logic is application code. Can be used by providers pre-emission or by a custom processor post-emission | +| `skillcard.yaml` schema validation (name, description, tags, version, authors, allowed-tools) | **YES** | Application-level schema validation — no framework involvement. A Zod or JSON Schema validator in the SDK package | +| Neo4j sync adapter interface (DEPENDS_ON, USES_TOOL, BELONGS_TO, SIMILAR_TO, IMPLEMENTED_BY) | **YES** | TypeScript interface definition — no framework involvement. The interface defines what implementors must provide; the actual Neo4j sync is RHIDP-15295 | +| SkillBundle metadata for Neo4j nodes and INCLUDES relationships | **YES** | Interface/type definition — application-level | +| Package follows semver with breaking-change policy | **YES** | Package management policy — no framework involvement | +| Existing boost providers refactored to implement the contract | **YES** | Application-level refactoring of existing code | +| README + API reference documentation | **YES** | Documentation exercise | + +**Verdict: FULLY FEASIBLE** — The SDK package is a standard npm package that wraps Backstage interfaces with AI Catalog-specific types and validation. It adds no new Backstage capabilities — it's an abstraction layer for connector developers. No upstream changes needed. + +--- + +### RHIDP-15261: Incremental (Delta) Sync Framework for Entity Providers + +**Summary:** After first full ingest, subsequent cycles only add/update/remove changed entities — delta sync with cursor/ETag persistence. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| --------------------------------------------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| SDK provides reusable incremental-sync framework translating changes to `applyMutation` calls | **YES** | Backstage directly supports delta mutations: `connection.applyMutation({ type: 'delta', added: [...], removed: [...] })`. The SDK wraps this with connector-friendly abstractions | +| Connectors report changes relative to a sync cursor (timestamp, ETag, change token) | **YES** | Application-level cursor management. The connector stores the cursor; the SDK framework manages cursor persistence | +| Framework persists sync cursors across polling cycles | **YES** | Cursor can be persisted in the Backstage database (via a dedicated table or the `KeyValueStore` service), or in a config-driven external store. No framework changes needed | +| Full re-ingest not required every cycle | **YES** | Delta mutation (`type: 'delta'`) is a first-class Backstage feature. Additionally, `IncrementalEntityProvider` supports paginated ingestion with cursor-based iteration | +| Fallback to full refresh when cursor invalid/expired | **YES** | Application logic — fall back to `type: 'full'` mutation when cursor is stale | +| Existing full-refresh providers work without modification | **YES** | Full mutation (`type: 'full'`) continues to work — the two mutation types coexist | + +**Verdict: FULLY FEASIBLE** — Delta sync is a first-class Backstage catalog feature. `applyMutation({ type: 'delta' })` handles explicit upserts/deletions. The `IncrementalEntityProvider` interface provides paginated cursor-based ingestion out of the box. The SDK wraps these with AI Catalog-specific conveniences. No upstream changes needed. + +**Key strength:** The Backstage `applyMutation` with `type: 'delta'` already handles the hard part — the SDK just needs to translate connector-level change events into the right mutation calls. + +--- + +### RHIDP-15263: Air-Gapped Deployment Support for AI Catalog Entity Providers + +**Summary:** Support custom CA bundles, K8s Secret-only credential references, configurable endpoint URLs, and startup validation rejecting plaintext credentials. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ------------------------------------------------------------------------------ | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Configurable registry endpoint URLs (no hardcoded SaaS endpoints) | **YES** | Standard app-config pattern: `catalog.providers..endpoint`. Entity providers read from config. No framework changes | +| Custom CA bundles via mounted Secret/ConfigMap honored for all TLS connections | **YES** | Standard Node.js TLS configuration. CA bundles are loaded from mounted paths and applied to HTTP clients (e.g., `node-fetch`, `undici`). The Backstage `UrlReader` service already supports TLS configuration. No framework changes | +| Credentials via Kubernetes Secret references only | **YES** | Standard RHDH pattern — `app-config.yaml` uses `$env:SECRET_NAME` references that resolve to mounted K8s Secret values. No framework changes | +| Plaintext credentials rejected with startup validation error | **YES** | Application-level startup check in the entity provider module. Inspect config values and reject if they look like plaintext (not `$env:` or `$secret:` references). No framework changes | +| Reference/example configuration for air-gapped pattern | **YES** | Documentation exercise | +| Validated against air-gapped OpenShift cluster | **YES** | Testing/validation exercise — no framework changes | + +**Verdict: FULLY FEASIBLE** — Air-gapped support is entirely about how entity providers configure their HTTP clients and credential loading. All patterns (CA bundles, Secret references, configurable endpoints) are standard Backstage/RHDH/K8s patterns. No upstream changes needed. + +**Note:** The Backstage `UrlReader` already supports `backend.reading.allow` for allowlisting hosts, and TLS configuration for custom CAs. Entity providers that use the UrlReader for registry communication get this for free. + +--- + +### RHIDP-15267: AI Catalog Performance Testing and Entity Resilience + +**Summary:** Validate that 5,000+ entity ingestion doesn't degrade catalog performance. Single-entity failures must not abort sync cycles. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ---------------------------------------------------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Load test with 5,000+ entities from a single connector | **YES** | Testing exercise. Entity providers can emit any number of entities via `applyMutation`. The catalog processes them through its pipeline | +| Catalog list/search API p95 latency ≤10% degradation vs. baseline | **YES** | Measurement exercise. The catalog uses PostgreSQL — query performance depends on indexing and query patterns, not framework limitations | +| Catalog processing-loop duration within RHDH SLA | **YES** | Measurement exercise. Processing-loop performance is well-understood for RHDH | +| Single-entity errors logged with context to identify offending source record | **YES** | Application-level error handling in entity providers. Wrap individual entity processing in try/catch, log with source identifiers, continue | +| Single-entity failures do not abort entire sync cycle | **YES** | Application-level resilience. The `IncrementalEntityProvider` interface already supports per-page error handling. For `applyMutation({ type: 'full' })`, filter out failed entities before mutation. For `type: 'delta'`, skip failed entities and continue | +| Load test scenarios sized against 1,945 skill images reference | **YES** | Test design exercise | +| Results documented and reproducible | **YES** | Documentation exercise | + +**Verdict: FULLY FEASIBLE** — Performance testing and resilience are application-level concerns. The Backstage catalog backend (PostgreSQL-based) can handle 5,000+ entities — the concern is about ensuring AI Catalog entity providers don't introduce inefficiencies. Entity-level error isolation is standard try/catch + continue in the provider code. No upstream changes needed. + +**Key consideration:** The `IncrementalEntityProvider` interface with its `rejectRemovalsAbovePercentage` safeguard is directly relevant — it protects against flaky sources that would otherwise wipe entity buckets. + +--- + +### RHIDP-15294: OCI Skill Registry Ingestion Framework + +**Summary:** Ingest skills from OCI container registries (Quay, GHCR, Docker Hub, Harbor, Artifactory, OpenShift Internal Image Registry) as `Resource` entities with `spec.type: ai-skill`. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| -------------------------------------------------------------------------------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Skills ingested as `Resource` entities with `spec.type: ai-skill` and `rhdh.io/ai-asset-category: skill` | **YES** | Standard entity emission via `applyMutation`. Resource kind is built-in, `ai-skill` is a free-form `spec.type` value, annotation is custom | +| Each entity includes OCI registry reference annotation (URL, namespace, image, digest) | **YES** | Custom annotations — fully supported | +| `skillcard.yaml` validated during ingestion, malformed artifacts rejected with logging | **YES** | Application-level validation using SDK's `skillcard.yaml` schema validator. Rejected entities are logged and excluded from `applyMutation` | +| Supports multiple OCI registries (Quay, GHCR, Docker Hub, Harbor, Artifactory, OpenShift) | **YES** | Application-level connector code. OCI Distribution Spec is the common API — the connector uses standard OCI APIs (manifest listing, blob download). Registry-specific auth may vary but is handled in connector configuration | +| Air-gapped support (configurable endpoints, CA bundles, K8s Secret credentials) | **YES** | Same patterns as RHIDP-15263. The OCI connector reads config for endpoint, CA path, and credential Secret reference | +| Incremental sync via OCI registry change detection (digest comparison, tag listing delta) | **YES** | Application-level delta detection. Compare current tag/digest listings against cached state. Use `applyMutation({ type: 'delta' })` for changes. No framework changes needed | +| Sync interval independently configurable via app-config | **YES** | Standard `catalog.providers..schedule` pattern | +| Packaged as RHDH dynamic plugin | **YES** | Standard RHDH dynamic plugin packaging — `@backstage/backend-plugin-api` module pattern. No upstream changes needed | + +**Verdict: FULLY FEASIBLE** — OCI registry ingestion is a new entity provider implementation. The Backstage framework provides all the extension points: `EntityProvider` interface, `applyMutation`, scheduled task runners, catalog module registration. The OCI-specific logic (registry API calls, manifest parsing, `skillcard.yaml` extraction) is application code. No upstream changes needed. + +**Key implementation detail:** The OCI Distribution Spec provides a common API across all target registries. The connector implements the OCI client, parses manifests, extracts `skillcard.yaml` from image layers, validates, and emits entities. This is connector engineering, not framework extension. + +--- + +### RHIDP-15295: Neo4j Knowledge Graph Sync Adapter for AI Catalog + +**Summary:** Secondary sync mechanism pushing skill/tool/domain/agent relationship data from the Backstage catalog to a Neo4j graph database. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ------------------------------------------------------------------------------------------ | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Sync reads AI asset entities from catalog and pushes to Neo4j | **YES — but no catalog extension point for this** | The Backstage catalog has no built-in "secondary sync" or "change notification" extension point. The adapter must either: (a) poll the catalog API periodically, or (b) hook into the entity processing pipeline via a custom processor that triggers Neo4j writes as a side effect | +| Supports relationship types: DEPENDS_ON, USES_TOOL, BELONGS_TO, SIMILAR_TO, IMPLEMENTED_BY | **YES** | Application-level — the adapter reads entity annotations and relations, then creates corresponding Neo4j relationships | +| Creates SkillBundle nodes and INCLUDES relationships | **YES** | Application-level — bundle definitions are read from entity metadata/annotations and translated to Neo4j nodes/edges | +| Sync is incremental (only changed entities trigger graph updates) | **YES with caveats** | If using catalog API polling, the adapter must track what it has already synced (via timestamps or entity revision). If using a processor-based approach, incrementality comes from the processing pipeline (processors run on entity changes). No framework change needed, but the polling approach requires careful cursor management | +| Sync failures for individual entities don't abort remaining sync | **YES** | Application-level try/catch + continue in the sync loop | +| Backstage catalog remains authoritative; Neo4j is derived | **YES** | Architectural constraint enforced by the adapter's read-only relationship with the catalog — it reads from the catalog API, never writes back | +| Implements SDK interface (RHIDP-15258) | **YES** | Application-level interface implementation | +| Documented with setup, graph schema, example Cypher queries | **YES** | Documentation exercise | + +**Verdict: FULLY FEASIBLE — but the catalog-to-Neo4j sync mechanism requires a design choice** + +The Backstage catalog does not have a built-in "secondary data store sync" extension point. Two implementation approaches: + +**Option A — Catalog API Polling (recommended):** +A scheduled task reads AI asset entities from the catalog search API, diffs against Neo4j state, and applies changes. This is the simplest approach and fully decoupled from catalog internals. + +- Pro: Clean separation, no coupling to catalog processing pipeline +- Pro: Uses stable public catalog APIs +- Con: Polling latency — changes are not reflected in Neo4j until the next poll cycle +- Con: Must manage its own change detection (entity revision tracking) + +**Option B — Custom Processor Side Effect:** +A custom `CatalogProcessor` runs in the processing pipeline and triggers Neo4j writes as entities are processed. Changes are reflected in Neo4j as soon as the entity is processed. + +- Pro: Near-real-time sync +- Pro: Incrementality is free — processors run on entity changes +- Con: Side effects in the processing pipeline are an anti-pattern — processors should emit relations and transform entities, not write to external systems +- Con: Couples the catalog processing loop to Neo4j availability — Neo4j downtime could impact catalog processing + +**Option C — Event-Driven (if available):** +If RHDH exposes catalog change events (via an event bus or webhook), the adapter subscribes and syncs on event receipt. This is the ideal approach but depends on RHDH's event infrastructure. + +**Recommendation:** Option A for initial implementation. Polling interval can be short (30s–60s) and the adapter is fully decoupled from catalog internals. If latency becomes an issue, evolve to Option C when RHDH event infrastructure is available. No upstream changes needed for any option. + +--- + +## Summary Matrix + +| Epic | Key | Feasible without upstream changes? | Implementation complexity | Notes | +| -------------------------------- | ----------- | ---------------------------------- | ------------------------- | ----------------------------------------------------------------------- | +| AI Asset Annotation Scheme | RHIDP-15254 | **YES** | Low | Standard custom annotations and spec.type values — explicitly supported | +| Entity-Provider SDK Package | RHIDP-15258 | **YES** | Medium | npm package wrapping Backstage interfaces — no framework changes | +| Incremental (Delta) Sync | RHIDP-15261 | **YES** | Medium | `applyMutation({ type: 'delta' })` is a first-class Backstage feature | +| Air-Gapped Deployment | RHIDP-15263 | **YES** | Medium | Standard K8s/Node.js TLS and credential patterns | +| Performance Testing & Resilience | RHIDP-15267 | **YES** | Medium | Testing/measurement exercise — no framework changes | +| OCI Skill Registry Ingestion | RHIDP-15294 | **YES** | High | New connector, but uses standard EntityProvider interface | +| Neo4j Knowledge Graph Sync | RHIDP-15295 | **YES** | High | No catalog secondary-sync extension point; recommend polling approach | + +## Key Findings + +1. **All 7 epics are fully feasible without upstream Backstage changes.** Unlike RHDHPLAN-1508 (where 3 epics required implementation deviations from the spec text), RHDHPLAN-1507's requirements align cleanly with Backstage catalog extension points. + +2. **The Backstage catalog is designed for exactly this kind of extension.** Custom annotations (low risk), custom `spec.type` values (low risk), custom entity providers (`EntityProvider` interface), delta mutations, incremental providers, custom processors — all are first-class extension mechanisms. + +3. **The critical design decision — using existing kinds (Resource, Component) instead of custom kinds — is already correct.** Custom entity kinds have "very large impact" because many plugins hard-code kind checks. Mapping AI assets to Resource and Component avoids this entirely. + +4. **Delta sync is not novel engineering — it's using the existing `applyMutation` API correctly.** The `type: 'delta'` mutation and the `IncrementalEntityProvider` interface handle the hard parts. The SDK wraps these with AI Catalog-specific abstractions. + +5. **The only area without a dedicated Backstage extension point is Neo4j sync (RHIDP-15295).** The catalog has no "secondary data store" hook. However, catalog API polling is a well-understood pattern and is fully decoupled from catalog internals. This is not a blocker — it's a design choice. + +6. **No PM discussion needed for RHDHPLAN-1507** (unlike RHDHPLAN-1508 where 3 areas required clarification). All acceptance criteria can be implemented as specified. + +## Comparison with RHDHPLAN-1508 + +| Aspect | RHDHPLAN-1507 (Entity Model) | RHDHPLAN-1508 (RBAC) | +| ------------------------------------ | -------------------------------------------------- | ---------------------------------------------------------------------------------- | +| Epics requiring deviations from spec | 0 of 7 | 3 of 7 | +| Upstream changes needed | None | None (but 3 require alternate approaches) | +| Framework alignment | High — uses catalog extension points as designed | Medium — permission framework lacks cascade, config toggle, and admin UI extension | +| PM discussion needed | No | Yes — policy cascade, default-deny config, admin UI placement | +| Highest-risk epic | RHIDP-15295 (Neo4j sync — no extension point) | RHIDP-15274 (Policy cascade — no framework support) | +| Implementation complexity | Straightforward for 5/7, high for 2/7 (OCI, Neo4j) | Medium for 4/7, high for 3/7 (cascade, admin UI, default-deny) | diff --git a/workspaces/boost/specifications/JIRA-analysis/RHDHPLAN-1508-feasibility-report.md b/workspaces/boost/specifications/JIRA-analysis/RHDHPLAN-1508-feasibility-report.md new file mode 100644 index 0000000000..b2134156e7 --- /dev/null +++ b/workspaces/boost/specifications/JIRA-analysis/RHDHPLAN-1508-feasibility-report.md @@ -0,0 +1,240 @@ +# RHDHPLAN-1508 Feasibility Report: RBAC Acceptance Criteria vs. Backstage Permission Framework + +**Date:** 2026-07-07 +**Purpose:** Assess whether each RHIDP epic under RHDHPLAN-1508 can be implemented within the existing Backstage permission/RBAC framework without requiring upstream changes. + +--- + +## Framework Capabilities Summary + +Before analyzing each epic, here is what the Backstage permission framework **does and does not** provide: + +| Capability | Status | +| -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | +| `PermissionPolicy.handle()` — flat per-request evaluation | **Available** | +| `createPermission()` / `permissionsRegistry.addPermissions()` | **Available** | +| `ALLOW` / `DENY` definitive decisions | **Available** | +| `CONDITIONAL` decisions (delegated to plugin backend) | **Available** — requires `resourceType` | +| `isResourcePermission()` type narrowing | **Available** | +| Custom rules via `apply(entity, params)` → boolean | **Available** — synchronous, single entity, no external calls | +| Rule composition via `anyOf` / `allOf` / `not` | **Available** — same entity only | +| `toQuery()` for database-level filtering | **Available** | +| `RequirePermission` frontend guard | **Available** | +| Per-plugin or per-resource-type config keys (e.g., `permission.rbac..defaultPolicy`) | **Not available** | +| Hierarchical/cascading policy evaluation (parent→child) | **Not available** | +| Recursive entity traversal in `apply()` | **Not available** — `apply()` is synchronous, receives one entity | +| Policy-driven config toggles (`allow`/`deny` enum values) | **Not available** — `permission.rbac.defaultPolicy` is a `$include` to a YAML file | +| RBAC admin UI extension points | **Not available** — the RBAC admin UI is a closed upstream plugin | + +--- + +## Epic-by-Epic Analysis + +### RHIDP-15270: AI Catalog Graduated Visibility Permissions + +**Summary:** Define and register `ai-catalog.asset.read`, `ai-catalog.asset.read.usage-docs`, and `ai-catalog.admin` permissions; implement a two-tier visibility model. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ---------------------------------------------------------------------------------------------------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| `ai-catalog.asset.read` defined via `createPermission` and registered via `permissionsRegistry.addPermissions()` | **YES** | Standard permission registration — boost already does this for its 16 lifecycle permissions | +| `ai-catalog.asset.read.usage-docs` defined and registered for field-level filtering | **YES** | Same registration pattern. Field-level filtering is a backend implementation detail — the permission gates access, the backend omits fields | +| `ai-catalog.admin` defined and registered, gating management actions | **YES** | Same registration pattern. What it gates is custom application logic, not framework behavior | +| Two-tier graduated visibility (Tier 1 = basic discovery, Tier 2 = usage docs) | **YES** | Two separate permissions with two `isResourcePermission` checks in the policy. Backend returns different field sets per tier. Standard pattern | +| Frontend uses `RequirePermission` to gate usage doc sections | **YES** | `RequirePermission` is a standard Backstage component for exactly this purpose | +| Users without Tier 2 see "request access" affordance | **YES** | Frontend conditional rendering based on permission check result — no framework changes needed | +| All three exported from common package | **YES** | Package export — no framework involvement | +| RBAC administrators can configure policies independently | **YES** | Standard RBAC policy configuration — each permission gets its own policy rules | +| Permission definitions documented with examples | **YES** | Documentation exercise | + +**Verdict: FULLY FEASIBLE** — All acceptance criteria use standard permission registration and policy patterns. No upstream changes required. This is the most straightforward epic. + +--- + +### RHIDP-15274: Version-Level Policy Cascade for AI Catalog Assets + +**Summary:** Asset-level RBAC policies cascade to all version entities unless a version-specific override exists. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| -------------------------------------------------------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Policy cascade evaluates version-level policies first; falls back to asset-level | **REQUIRES CUSTOM IMPLEMENTATION** | The framework evaluates each permission request against a single entity. There is no built-in "check for version-specific policy, then fall back to parent asset policy." This requires custom `PermissionPolicy` logic that performs catalog lookups during evaluation | +| Asset-level policies auto-apply to all versions when no version override set | **REQUIRES CUSTOM IMPLEMENTATION** | Same — the policy must resolve the parent asset entity and check its policies. `apply()` is synchronous and cannot make catalog API calls | +| Version-specific override takes precedence for that version only | **REQUIRES CUSTOM IMPLEMENTATION** | The precedence logic itself is straightforward code, but it requires the policy to know about the entity hierarchy | +| New versions inherit asset-level policies immediately upon creation | **REQUIRES CUSTOM IMPLEMENTATION** | Same cascade logic — no manual policy assignment needed means the cascade must execute at query time | +| Removing version override causes revert to asset-level policy | **REQUIRES CUSTOM IMPLEMENTATION** | Follows from the cascade logic | +| Documented for RBAC administrators | **YES** | Documentation exercise | +| Unit and integration tests | **YES** | Testing exercise | + +**Verdict: FEASIBLE WITH SIGNIFICANT CUSTOM WORK — NO UPSTREAM CHANGES NEEDED, BUT NOT A FRAMEWORK FEATURE** + +The cascade can be implemented without modifying the Backstage framework itself, but it requires building custom infrastructure: + +**Option A — Custom PermissionPolicy with catalog lookups:** +The `handle()` method is async (`Promise`), so it _can_ make catalog API calls during evaluation. The policy would: + +1. Receive a permission request for a version entity +2. Check if a version-specific policy exists (via stored policy rules) +3. If not, look up the parent asset entity via the catalog API (using `rhdh.io/ai-asset-version` annotation or `versionOf` relation) +4. Evaluate the asset-level policy rules against the parent entity + +This is doable but has performance implications — every version-entity permission check requires a catalog lookup. + +**Option B — Denormalized policies at ingestion time:** +When a version entity is ingested, copy the parent asset's policy rules to the version entity's metadata. Policy evaluation then checks the version entity's own metadata without needing to traverse the hierarchy. Override by writing version-specific rules that shadow the inherited ones. + +This avoids runtime catalog lookups but requires the entity provider to maintain policy synchronization. + +**Recommendation:** Option B is more performant and avoids the `apply()` synchronous limitation. The entity provider already has access to the catalog and can denormalize policies during ingestion. This is entirely within the boost workspace — no upstream changes needed. + +--- + +### RHIDP-15276: Default-Deny Policy Configuration for AI Catalog Assets + +**Summary:** Configurable default-deny/allow setting for newly ingested assets, with per-category and per-connector scoping, gated by `ai-catalog.admin`. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ---------------------------------------------------------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `permission.rbac.aiCatalog.defaultPolicy` accepts `allow`/`deny` | **NOT AS SPECIFIED** — but achievable differently | This specific config key pattern doesn't exist in the RBAC plugin. However, the same outcome is achievable through a catch-all RBAC policy rule (see below) | +| Only accessible to `ai-catalog.admin` holders | **YES** | The `ai-catalog.admin` permission gates who can modify the policy — standard permission check | +| Per-category defaults (`rhdh.io/ai-asset-category`) | **YES with custom rules** | A custom permission rule can inspect entity annotations to match categories. Combined with a conditional decision, this filters per-category | +| Per-connector defaults (different source connectors) | **YES with custom rules** | Same approach — a custom rule inspects a source-connector annotation on the entity | +| Default-deny: newly ingested entities not visible until explicit ALLOW | **YES** | A catch-all DENY conditional rule for `ai-catalog.asset.read` achieves this. Entities are invisible until an explicit ALLOW policy overrides the catch-all | +| Default-allow: entities visible to all users with the permission | **YES** | Absence of the catch-all deny rule = default-allow. Standard RBAC behavior | +| Changing default only affects subsequently ingested assets | **REQUIRES CUSTOM IMPLEMENTATION** | The RBAC framework doesn't distinguish "ingested before/after a policy change." This requires either: (a) marking entities with an ingestion-time annotation that the policy checks, or (b) accepting that the policy change affects all entities (which is simpler and arguably more correct) | +| Log entry when asset ingested under default-deny | **YES** | Application-level logging in the entity provider — no framework involvement | +| Audit log for changing the setting | **YES** | Application-level audit logging — covered by RHIDP-15277 | +| Tests for both postures, per-category, per-connector | **YES** | Testing exercise | + +**Verdict: FEASIBLE WITHOUT UPSTREAM CHANGES — using catch-all policy approach (Option 2 from questions doc)** + +The implementation path: + +1. Define custom permission rules: `isAiAssetCategory({ category })` and `isFromConnector({ connector })` that inspect entity annotations +2. Default-deny = a catch-all conditional DENY rule for `ai-catalog.asset.read` that matches all AI catalog entities +3. Per-category defaults = conditional rules scoped to specific `rhdh.io/ai-asset-category` values +4. Per-connector defaults = conditional rules scoped to a source-connector annotation +5. The `ai-catalog.admin` permission gates who can create/modify these policy rules + +**Key deviation from spec:** The config key `permission.rbac.aiCatalog.defaultPolicy: allow|deny` is replaced by the presence/absence of catch-all deny policy rules. The operational outcome is identical — the mechanism is RBAC policies rather than a config toggle. The one criterion that requires custom work is "only affects subsequently ingested assets" — this is unusual for an RBAC system (policies typically apply to all matching entities) and should be discussed with PM. + +--- + +### RHIDP-15277: AI Catalog RBAC Audit Logging + +**Summary:** Audit logging for RBAC policy changes, `ai-catalog.admin` management actions, and entity provider ingestion sync events. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ----------------------------------------------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| RBAC policy change events (timestamp, actor, action, target, previous/new value) | **YES** | Application-level structured logging when policy CRUD operations occur. Boost already emits structured logs for lifecycle transitions | +| `ai-catalog.admin` management events (posture changes, category/connector policy changes) | **YES** | Application-level logging around the management API endpoints. These are boost-owned endpoints, not upstream | +| Ingestion sync events (provider, counts, duration, errors) | **YES** | Application-level logging in the entity provider. Entity providers are boost-owned | +| Individual asset ingestion events (entity ref, operation, source) | **YES** | Same — entity provider logging | +| Structured JSON format compatible with RHDH log aggregation | **YES** | Log format is an application concern. RHDH already uses structured JSON logging | +| Dedicated audit log channel (not mixed with debug logs) | **YES** | Backstage supports multiple logger instances. A dedicated `audit` logger can be created | +| Sufficient context without exposing sensitive content | **YES** | Log content design — application-level concern | +| Unit tests for event emission | **YES** | Testing exercise | + +**Verdict: FULLY FEASIBLE** — Audit logging is entirely an application-level concern. All events are emitted by boost-owned code (policy management endpoints, entity providers, management APIs). No upstream changes needed. + +--- + +### RHIDP-15281: AI Catalog RBAC Performance and Multi-Tenancy + +**Summary:** Performance testing at scale and multi-tenant filtering using policy-based tenant scoping. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ------------------------------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Performance test suite at 100/500/1000 asset scale | **YES** | Testing infrastructure — no framework changes | +| Performance baselines for unfiltered, single-permission, two-tier queries | **YES** | Measurement exercise | +| Permission-check latency overhead documented | **YES** | Documentation exercise | +| Multi-tenant filtering via policy-based tenant scoping | **YES with custom rules** | A custom permission rule `isInTenant({ tenant })` can inspect entity namespace or a tenant annotation. The conditional decision filters entities per tenant at the database level via `toQuery()` | +| Single entity provider serves multiple tenants | **YES** | Entity provider design — application-level | +| No per-tenant re-ingestion needed | **YES** | Follows from policy-based filtering — the same entities exist once, visibility is controlled by policy | +| Performance tests reproducible and CI-integratable | **YES** | CI/testing infrastructure | + +**Verdict: FULLY FEASIBLE** — Performance testing is infrastructure work. Multi-tenant filtering maps cleanly to custom permission rules with conditional decisions. The `toQuery()` mechanism enables database-level tenant filtering, which is exactly how Backstage envisions conditional decisions being used at scale. No upstream changes needed. + +--- + +### RHIDP-15304: RBAC Admin UI Section for AI Catalog Policy Management + +**Summary:** Dedicated AI Catalog section in the RHDH RBAC admin UI for SMP Admins to manage visibility policies without writing YAML. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ---------------------------------------------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| RBAC admin UI includes "AI Catalog" section visible only to `ai-catalog.admin` holders | **DEPENDS ON APPROACH** | Cannot add a section to the upstream Backstage RBAC plugin UI without upstream changes. However, a **standalone AI Catalog admin page** within the boost frontend achieves the same outcome | +| UI allows setting visibility policies scoped to individual skill, category, or connector | **YES — as standalone page** | A boost-owned admin page can present RBAC policy CRUD operations with AI Catalog-specific form fields | +| UI displays current default posture per category/connector with controls to change | **YES — as standalone page** | The page reads current RBAC policies and presents them grouped by category/connector | +| UI supports creating/editing/deleting conditional policies without YAML | **YES — as standalone page** | The page translates form inputs into RBAC policy API calls — no YAML editing | +| UI shows summary of all active AI Catalog policies | **YES — as standalone page** | Read from RBAC policy storage, filter to AI catalog permissions, group and display | +| Changes produce audit log entries | **YES** | The management API emits audit events — covered by RHIDP-15277 | +| Follows RHDH admin UI patterns (MUI, Backstage theme) | **YES** | Standard frontend development using Backstage component library | + +**Verdict: FEASIBLE AS A STANDALONE ADMIN PAGE — NOT as a section added to the upstream RBAC admin UI** + +The key deviation: RHDHPLAN-1508 says "dedicated section in the RHDH RBAC admin UI" but the RBAC admin UI is an upstream plugin. Two paths: + +1. **Standalone AI Catalog admin page** (recommended): A new route in the boost frontend (`/ai-catalog/admin/rbac`) that provides purpose-built AI Catalog policy management. It can link from the main RBAC admin UI via a sidebar item or banner, but it's a separate page owned by the boost plugin. No upstream changes needed. + +2. **RHDH downstream RBAC plugin extension**: If RHDH's downstream fork of the RBAC plugin already has extension points or customizations, a section could be added there — but this is an RHDH platform team concern, not a boost workspace concern, and still involves modifying the RBAC plugin. + +**Recommendation:** Option 1. The SMP Admin persona doesn't care whether the policy management UI is a tab inside the RBAC page or a linked page — they care that it exists and is easy to use. A standalone page avoids upstream dependencies entirely. + +--- + +### RHIDP-15305: SkillBundle RBAC Filtering at Read Time + +**Summary:** SkillBundle detail views filter out skills the current user cannot see via `ai-catalog.asset.read`. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| -------------------------------------------------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Bundle response filters out skills user lacks `ai-catalog.asset.read` on | **YES** | Backend API logic: when building the bundle response, check `ai-catalog.asset.read` for each included skill entity and omit those that return DENY | +| Filtering at read time by backend, not storage time | **YES** | Application-level API design — the Neo4j query returns all skills in the bundle, the backend filters before responding | +| Automated test: mixed permitted/restricted skills returns only permitted | **YES** | Testing exercise | +| Bundle metadata reflects filtered view (count = visible skills, not total) | **YES** | Backend response construction — recalculate count after filtering | +| Documentation: bundle contents may differ per viewer | **YES** | Documentation exercise | +| Read-time filtering adds no more than 10% latency | **DEPENDS ON IMPLEMENTATION** | Batch permission checks for all skills in a bundle are feasible via `authorizeConditional()` which can evaluate multiple entities against the same permission in one call. Performance depends on bundle size and policy complexity | + +**Verdict: FULLY FEASIBLE** — Read-time filtering is standard backend logic. The Backstage permission framework supports batch authorization via `authorizeConditional()`, which can evaluate multiple entities in a single call. The bundle API reads from Neo4j, then filters through the permission framework before responding. No upstream changes needed. + +--- + +## Summary Matrix + +| Epic | Key | Feasible without upstream changes? | Implementation complexity | Notes | +| -------------------------------- | ----------- | ---------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| Graduated Visibility Permissions | RHIDP-15270 | **YES** | Low | Standard permission registration and policy patterns | +| Version-Level Policy Cascade | RHIDP-15274 | **YES — with custom work** | High | Requires custom cascade logic; recommend denormalized policies at ingestion time (Option B) | +| Default-Deny Configuration | RHIDP-15276 | **YES — via catch-all policy** | Medium | Config key pattern replaced by catch-all RBAC policy rules; one criterion ("only affects new assets") needs PM discussion | +| RBAC Audit Logging | RHIDP-15277 | **YES** | Medium | Entirely application-level logging in boost-owned code | +| Performance & Multi-Tenancy | RHIDP-15281 | **YES** | Medium | Custom tenant-scoping rules; performance testing is infrastructure | +| RBAC Admin UI Section | RHIDP-15304 | **YES — as standalone page** | Medium-High | Cannot modify upstream RBAC plugin UI; standalone admin page achieves same outcome | +| SkillBundle Read-Time Filtering | RHIDP-15305 | **YES** | Medium | Standard backend filtering using batch permission checks | + +## Key Findings + +1. **All 7 epics have implementation paths that do NOT require upstream Backstage changes.** However, 3 epics require implementation approaches that deviate from the exact wording of RHDHPLAN-1508. + +2. **Three areas require PM discussion:** + - **RHIDP-15274 (Policy Cascade):** The framework has no cascade — must be built as custom logic. Recommend denormalized policies (set at ingestion time) rather than runtime cascade for performance. + - **RHIDP-15276 (Default-Deny Config):** The `permission.rbac.aiCatalog.defaultPolicy` config key pattern doesn't exist. Recommend catch-all RBAC policy rules instead. Also, "only affects subsequently ingested assets" is atypical for RBAC — policies normally apply to all matching entities. + - **RHIDP-15304 (Admin UI):** Cannot add sections to the upstream RBAC admin UI. Recommend a standalone AI Catalog admin page linked from the main navigation. + +3. **The catch-all policy pattern (Option 2) is the recurring solution** for multiple criteria across RHIDP-15270, RHIDP-15276, and RHIDP-15305. It's the framework-aligned way to achieve default-deny, category-scoped, and connector-scoped policy behaviors. + +4. **Custom permission rules are the main building block.** Rules like `isAiAssetCategory()`, `isFromConnector()`, `isInTenant()` inspect entity annotations and produce conditional decisions that filter at the database level via `toQuery()`. This is exactly how the framework is designed to be extended. + +5. **Performance risk concentrates in RHIDP-15274.** Runtime cascade (looking up parent asset during every version-entity permission check) conflicts with the <10% latency overhead requirement. Denormalization at ingestion time avoids this but adds entity provider complexity. diff --git a/workspaces/boost/specifications/JIRA-analysis/RHDHPLAN-1510-feasibility-report.md b/workspaces/boost/specifications/JIRA-analysis/RHDHPLAN-1510-feasibility-report.md new file mode 100644 index 0000000000..073ec8cf5b --- /dev/null +++ b/workspaces/boost/specifications/JIRA-analysis/RHDHPLAN-1510-feasibility-report.md @@ -0,0 +1,164 @@ +# RHDHPLAN-1510 Feasibility Report: Connector Acceptance Criteria vs. Backstage Catalog Framework + +**Date:** 2026-07-07 +**Purpose:** Assess whether each RHIDP epic under RHDHPLAN-1510 can be implemented within the existing Backstage catalog framework without requiring upstream changes. + +--- + +## Framework Capabilities Summary + +RHDHPLAN-1510 is about connector implementations — entity providers that ingest AI assets from external registries into the Backstage Software Catalog. The cross-reference targets are the Backstage entity provider interface, catalog extension points, and deployment infrastructure patterns. + +| Capability | Status | +| ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| `EntityProvider` interface (`connect`, `applyMutation`) | **Available** | +| Full mutation (`type: 'full'`) | **Available** | +| Delta mutation (`type: 'delta'`) | **Available** | +| `IncrementalEntityProvider` (paginated cursor-based ingestion) | **Available** | +| Scheduled task runners (`SchedulerService`) | **Available** | +| App-config-driven provider schedule (`catalog.providers..schedule`) | **Available** | +| Per-provider isolated entity buckets | **Available** | +| `locationKey` conflict resolution | **Available** | +| Custom annotations (`metadata.annotations`) | **Available** | +| Custom `spec.type` values (free-form) | **Available** | +| Mapping to built-in kinds (Resource, Component) | **Available** | +| Dynamic plugin packaging for RHDH | **Available** | +| `catalogProcessingExtensionPoint` for provider registration | **Available** | +| Custom CA bundle handling | **Not built-in at catalog level** — standard Node.js/K8s TLS patterns | +| K8s Secret credential injection | **Not built-in at catalog level** — standard `$env:`/`$secret:` app-config patterns | +| OCI registry client | **Not built-in** — requires custom implementation or library | +| RHOAI/Kubeflow Model Registry client | **Not built-in** — requires custom implementation | +| MCP Registry mirror support | **Not built-in** — requires custom endpoint configuration | +| Cross-connector fault isolation | **Not built-in** — each provider runs in its own bucket but shares the Node.js process | + +--- + +## Epic-by-Epic Analysis + +### RHIDP-15313: MCP Registry Connector — Productization & Air-Gapped Support + +**Summary:** Productize the upstream MCP Registry entity provider (RHDHPLAN-393) with air-gapped support: customer-mirrored registries, custom CA bundles, K8s Secret auth, RHDH AI Asset annotation enrichment. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ---------------------------------------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| Configurable target endpoint overriding `registry.modelcontextprotocol.io` | **YES** | Standard app-config pattern: `catalog.providers.mcpRegistry.endpoint`. The entity provider reads the endpoint from config. No framework changes | +| Zero outbound requests to public endpoint when mirror configured | **YES** | Application-level — the connector uses only the configured endpoint URL. Validation test confirms no public endpoint traffic | +| Custom CA bundles from K8s Secret/ConfigMap mounts for mirrored registry TLS | **YES** | Standard Node.js TLS configuration. Load CA from mounted file path, pass to HTTP client. Shared utility from RHIDP-15316 | +| K8s Secret-based credentials for private/authenticated registries | **YES** | Standard RHDH pattern — `$env:` references in app-config resolve to mounted K8s Secret values | +| Entities carry `rhdh.io/ai-asset-category`, `rhdh.io/ai-asset-version` annotations | **YES** | Custom annotations set during entity emission. The productization layer enriches entities emitted by the upstream provider before `applyMutation` | +| Integrates with 1507's shared ingestion framework/SDK validation | **YES** | Application-level — the connector uses the SDK's validation functions before emitting entities | + +**Verdict: FULLY FEASIBLE** — This epic is a productization wrapper around the upstream MCP Registry connector (RHDHPLAN-393). It adds configuration, credential handling, and annotation enrichment — all standard patterns. The upstream connector handles the core `/v1/servers` API integration; this epic adds the deployment hardening. No upstream Backstage changes needed. + +**Dependency note:** Requires RHDHPLAN-393 (upstream MCP Registry entity provider) to be implemented first. This epic layers on top, not replaces. + +--- + +### RHIDP-15314: RHOAI Entity-Provider Connector (Model Registry + MCP Catalog) + +**Summary:** Entity provider with two toggleable sources: (1) RHOAI Model Registry API (`RegisteredModel`/`ModelVersion` → `ai-model`/`model-server`), (2) RHOAI 3.4 MCP catalog → `mcp-server`. Cross-cluster, K8s Secrets, custom CA. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Model Registry source connects to Kubeflow `RegisteredModel`/`ModelVersion` API, emits `ai-model`/`model-server` entities | **YES** | Standard entity provider implementation. The connector calls the Kubeflow Model Registry REST API, maps responses to Backstage entities (Resource with `spec.type: ai-model`, Component with `spec.type: model-server`), and emits via `applyMutation`. Prior art: `redhat-ai-dev/model-catalog-bridge` | +| RHOAI version normalization maps `ModelVersion` to `rhdh.io/ai-asset-version` | **YES** | Application-level mapping logic — extract version identifier from Kubeflow API response, set as annotation value | +| MCP catalog source connects to RHOAI 3.4 MCP catalog API, emits `mcp-server` entities | **YES** | Same pattern — REST API call, entity mapping, `applyMutation`. The RHOAI 3.4 MCP catalog API is a new RHOAI endpoint | +| MCP catalog source gracefully handles absence on earlier RHOAI versions | **YES** | Application-level — attempt API call, catch 404/connection error, log warning, disable MCP source for this cycle. Standard resilience pattern | +| Model Registry and MCP catalog sources independently toggleable | **YES** | App-config-driven: `catalog.providers.rhoai.modelRegistry.enabled: true/false` and `catalog.providers.rhoai.mcpCatalog.enabled: true/false`. Provider reads config at startup | +| Cross-cluster endpoint with K8s Secret credentials and custom CA bundles | **YES** | Standard patterns — configurable endpoint URL, `$env:` Secret references, CA bundle from mounted path. Shared utilities from RHIDP-15316 | + +**Verdict: FULLY FEASIBLE** — This is a standard entity provider implementation against the Kubeflow Model Registry and RHOAI MCP catalog APIs. The Backstage `EntityProvider` interface handles everything needed. The `redhat-ai-dev/model-catalog-bridge` provides architectural prior art. No upstream Backstage changes needed. + +**Key implementation note:** Two independently toggleable sources can be implemented as either (a) two separate `EntityProvider` instances (simpler, each with its own bucket), or (b) a single provider that conditionally fetches from both sources and emits all entities in one `applyMutation`. Option (a) is cleaner — each source has its own isolated bucket and independent failure domains. + +--- + +### RHIDP-15315: OCI Skill Registry Entity-Provider Connector + +**Summary:** Discover AI skills published as OCI artifacts, parse `skillcard.yaml`, validate, emit as `ai-skill` entities. Incremental sync via digest, caching, validated at 2,000-image scale. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Connects to any OCI Distribution spec-compliant registry at configurable URL/namespace | **YES** | Application-level — OCI client implementation using the OCI Distribution Spec APIs (tag listing, manifest fetch, blob download). No Backstage framework involvement | +| Discovers skill repositories and fetches OCI manifests and annotations | **YES** | OCI Distribution Spec: `GET /v2//tags/list`, `GET /v2//manifests/`. Standard HTTP API calls | +| Parses and validates `skillcard.yaml` required fields | **YES** | Application-level — extract `skillcard.yaml` from OCI image layer, parse YAML, validate against SDK's schema validator (RHIDP-15258) | +| Invalid skills rejected with descriptive errors without aborting sync | **YES** | Application-level try/catch per skill. Log error with registry/namespace/image reference, skip invalid, continue | +| Emits `Resource` entities with `spec.type: ai-skill` and RHDH AI Asset annotations including OCI registry reference | **YES** | Standard entity emission via `applyMutation`. Resource is built-in, `ai-skill` is free-form `spec.type`, annotations are custom | +| Incremental sync via manifest-digest change detection with 5-minute TTL in-memory cache and disk backup | **YES** | Application-level caching. Compare current manifest digests against cached digests. Only re-process changed skills. Use `applyMutation({ type: 'delta' })` for changes. In-memory Map + disk-persisted JSON for cache durability | +| K8s pull secret as primary credential, custom CA bundles | **YES** | Standard patterns — pull secret parsed for registry auth (same format as Docker `config.json`), CA from mounted path | +| Full sync of 2,000 images within 5 minutes using manifest-only fetching | **YES** | Performance target — manifest-only fetching (no blob download during discovery) keeps per-image cost low. At 2,000 images ÷ 300 seconds = ~6.7 images/second, achievable with parallel manifest fetches | + +**Verdict: FULLY FEASIBLE** — This is a new entity provider that implements an OCI registry client. The Backstage framework provides the `EntityProvider` interface and `applyMutation`; everything else is OCI-specific application code. No upstream Backstage changes needed. + +**Implementation note:** This epic overlaps significantly with RHDHPLAN-1507's RHIDP-15294 (OCI Skill Registry Ingestion Framework). RHIDP-15294 defines the framework; this epic (RHIDP-15315) appears to be the concrete connector implementation. They may need to be reconciled to avoid duplicate work — the framework epic defines the SDK interfaces and validation; the connector epic implements the actual OCI client and entity provider. + +--- + +### RHIDP-15316: Cross-Connector Shared Infrastructure + +**Summary:** Shared utilities for all three connectors: CA bundle resolution, K8s Secret credential injection, connector enable/disable pattern, fault isolation. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| -------------------------------------------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Shared utility loads CA bundles from K8s Secret/ConfigMap mounts, configurable per connector | **YES** | Utility function: read CA file from configured mount path, create `https.Agent` with custom CA, expose for connectors to use in their HTTP clients. Standard Node.js pattern | +| One connector failure does not block other connectors or degrade non-AI catalog entities | **YES** | Each entity provider runs in its own isolated bucket. Provider failures are scoped to their bucket — other providers and catalog entities are unaffected. The Backstage processing pipeline handles provider errors gracefully. Additional hardening: wrap each provider's `run()` in try/catch to prevent unhandled rejections from crashing the process | +| Actionable error details logged per connector on failure | **YES** | Application-level structured logging — each connector logs with its provider name, endpoint, error message, and retry status | +| Connector enable/disable config pattern consistent across all connectors | **YES** | Standard app-config pattern: `catalog.providers..enabled: true/false`. Each provider checks config at registration time and skips registration if disabled | + +**Verdict: FULLY FEASIBLE** — This epic is shared utility code. CA bundle loading, Secret injection, error handling, and enable/disable config are all standard Node.js and Backstage patterns. No upstream changes needed. + +**Key design decisions:** + +1. **CA bundle utility** — A shared function like `loadCaBundle(config: Config, connectorId: string): Buffer | undefined` that reads `catalog.providers..tls.caFile` or `catalog.providers..tls.caSecret` and returns the CA certificate for use in `https.Agent`. + +2. **Fault isolation** — Backstage's per-provider entity buckets already provide data isolation. The shared infrastructure adds process-level isolation: `try/catch` + structured error logging in the scheduled task runner wrapper, ensuring one connector's unhandled error doesn't crash the Node.js process. + +3. **Enable/disable** — The backend module's `init()` function checks `catalog.providers..enabled` before calling `catalog.addEntityProvider()`. Disabled connectors are never registered. + +--- + +## Summary Matrix + +| Epic | Key | Feasible without upstream changes? | Implementation complexity | Notes | +| ------------------------------------- | ----------- | ---------------------------------- | ------------------------- | ------------------------------------------------------------------------------------ | +| MCP Registry — Productization | RHIDP-15313 | **YES** | Low-Medium | Productization wrapper on upstream connector; depends on RHDHPLAN-393 | +| RHOAI Connector | RHIDP-15314 | **YES** | Medium | Two-source provider against Kubeflow + RHOAI APIs; prior art exists | +| OCI Skill Registry Connector | RHIDP-15315 | **YES** | High | OCI client implementation + caching + 2K-scale validation; overlaps with RHIDP-15294 | +| Cross-Connector Shared Infrastructure | RHIDP-15316 | **YES** | Low | Shared utilities — CA, Secrets, fault isolation, config patterns | + +## Key Findings + +1. **All 4 epics are fully feasible without upstream Backstage changes.** Every acceptance criterion maps to standard entity provider patterns or application-level code. No spec deviations needed. + +2. **RHDHPLAN-1510 is the most straightforward of the three features analyzed so far.** These are concrete connector implementations using the `EntityProvider` interface that RHDHPLAN-1507 establishes the SDK for. The Backstage catalog framework is designed for exactly this pattern. + +3. **Potential overlap between RHIDP-15315 (this feature) and RHIDP-15294 (RHDHPLAN-1507).** Both describe OCI Skill Registry ingestion. RHIDP-15294 defines the "ingestion framework" and RHIDP-15315 is the "entity-provider connector." These should be reconciled — if 15294 delivers the OCI client, caching, and `skillcard.yaml` parsing as reusable framework code, then 15315 is the thin entity-provider wrapper that uses it. If not reconciled, there's a risk of duplicate implementation. + +4. **The cross-connector shared infrastructure (RHIDP-15316) is a good engineering practice but not a framework requirement.** Backstage doesn't mandate shared utilities across providers — each provider can implement its own CA/Secret handling. The shared approach reduces duplication and ensures consistency, which is valuable but entirely an application architecture choice. + +5. **No PM discussion needed.** All acceptance criteria can be implemented exactly as specified. This contrasts with RHDHPLAN-1508 (3 areas needing PM discussion) and aligns with RHDHPLAN-1507 (also no PM discussion needed). + +6. **Dependency chain is clear and manageable:** + - RHIDP-15316 (shared infra) has no dependencies — build first + - RHIDP-15313 (MCP Registry) depends on RHDHPLAN-393 (upstream connector) + RHIDP-15316 + - RHIDP-15314 (RHOAI) depends on RHIDP-15316 + - RHIDP-15315 (OCI) depends on RHIDP-15316 + RHIDP-15294 (from RHDHPLAN-1507) + +## Comparison Across Features + +| Aspect | RHDHPLAN-1507 (Entity Model) | RHDHPLAN-1508 (RBAC) | RHDHPLAN-1510 (Connectors) | +| ------------------------------------ | ----------------------------- | ----------------------------------------- | --------------------------------------- | +| Epics requiring deviations from spec | 0 of 7 | 3 of 7 | 0 of 4 | +| Upstream changes needed | None | None (but 3 require alternate approaches) | None | +| Framework alignment | High | Medium | High | +| PM discussion needed | No | Yes (3 areas) | No | +| Highest-risk epic | RHIDP-15295 (Neo4j sync) | RHIDP-15274 (Policy cascade) | RHIDP-15315 (OCI scale, overlaps 15294) | +| Implementation complexity | Straightforward 5/7, high 2/7 | Medium 4/7, high 3/7 | Straightforward 3/4, high 1/4 | diff --git a/workspaces/boost/specifications/JIRA-analysis/RHDHPLAN-1513-feasibility-report.md b/workspaces/boost/specifications/JIRA-analysis/RHDHPLAN-1513-feasibility-report.md new file mode 100644 index 0000000000..9a7037e48a --- /dev/null +++ b/workspaces/boost/specifications/JIRA-analysis/RHDHPLAN-1513-feasibility-report.md @@ -0,0 +1,145 @@ +# RHDHPLAN-1513 Feasibility Report: Operational Infrastructure Criteria vs. Backstage Framework + +**Date:** 2026-07-07 +**Purpose:** Assess whether each RHIDP epic under RHDHPLAN-1513 can be implemented within the existing Backstage framework and boost infrastructure without requiring upstream changes. + +--- + +## Framework Capabilities Summary + +RHDHPLAN-1513 covers operational infrastructure for the AI Catalog: admin dashboard, hot-reload config, audit logging/metrics, and upstream schema alignment. The cross-reference targets are the Backstage frontend extension model, configuration system, logging infrastructure, and catalog entity model, alongside boost's own `RuntimeConfigResolver` infrastructure. + +| Capability | Status | +| ------------------------------------------------------------ | ------------------------------------------------------------------------------ | +| Backstage frontend plugin pages (routes, sidebar items) | **Available** — standard frontend plugin pattern | +| Backstage admin page patterns | **Available** — admin pages are standard routes gated by permissions | +| `RequirePermission` frontend guards | **Available** | +| Backstage app-config system (`ConfigApi`) | **Available** — read-only at runtime; changes require restart | +| Backstage config hot-reload | **Not built-in** — app-config is loaded at startup; no native hot-reload | +| Boost `RuntimeConfigResolver` (30s TTL, YAML + DB two-layer) | **Available** — boost-internal, already proven | +| Backstage backend API routes (`createBackendPlugin`) | **Available** — standard REST endpoint pattern | +| Backstage structured logging (`LoggerService`) | **Available** — JSON structured logs | +| Backstage audit logging infrastructure | **Limited** — RHDH has audit logging; upstream Backstage has basic logger only | +| Backstage catalog search/list APIs | **Available** — for entity enumeration | +| Custom annotations / `spec.type` values | **Available** — free-form extension | +| Upstream Backstage RFCs (#32062, #33060) | **In progress** — not finalized, no migration tooling exists | + +--- + +## Epic-by-Epic Analysis + +### RHIDP-15331: Ingestion Health Admin Dashboard + +**Summary:** Admin-facing per-connector health view: enabled/disabled, sync timestamps, health status, error classification, "Force Sync" action, Neo4j graph sync panel. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ---------------------------------------------------------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Per-connector health dashboard showing enabled/disabled, last sync, health status, most recent error | **YES** | Frontend page in the boost admin panel. Backend API exposes connector status from stored state (last sync time, error, health). Connectors already run via scheduled tasks — the task runner captures this state. No framework changes needed | +| Actionable error classification (auth, network, schema, rate limiting) | **YES** | Application-level error categorization. Connectors catch errors, classify by type (HTTP 401 → auth, connection refused → network, validation failure → schema, HTTP 429 → rate limiting), store classification alongside error message | +| "Force Sync" action per connector | **YES** | Backend API endpoint that triggers an immediate connector sync cycle. Entity providers run via `SchedulerService` task runners — a force-sync endpoint invokes the provider's `run()` method outside the scheduled cadence. No framework changes needed | +| Neo4j graph sync status panel with "Force Re-sync" | **YES** | Same pattern — backend API exposes Neo4j sync adapter status, frontend displays it, force-sync endpoint triggers adapter run | +| Disconnected-cluster health differentiation (disabled vs. failing) | **YES** | Application-level — config check (disabled = intentional, `enabled: false` in config) vs. runtime error (failing = connector enabled but erroring). Display different icons/labels | + +**Verdict: FULLY FEASIBLE** — This is a standard admin dashboard page. The backend stores connector health state (written by connectors during sync cycles), exposes it via REST API, and the frontend renders it. The "Force Sync" action triggers the provider's run method. All within standard Backstage frontend/backend plugin patterns. No upstream changes needed. + +**Implementation note:** Boost's admin panel already has model connection, system prompt, agent config, and skills marketplace sections. Adding an "Ingestion Health" section follows the existing pattern — new route, new backend API, new frontend components. + +--- + +### RHIDP-15332: Connector Configuration Hot-Reload + +**Summary:** Extend `RuntimeConfigResolver` to connector settings: enable/disable, endpoint URLs, sync schedules. Changes within 30s TTL without pod restart. Zod schemas for all fields. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ------------------------------------------------------------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Zod schemas defined for per-connector settings (enabled, endpoint URL, schedule, credential references) | **YES** | Application-level schema definitions. Boost already uses Zod for config validation. Adding connector-specific schemas follows the existing pattern | +| Each field annotated with `configScope` | **YES** | Boost's `RuntimeConfigResolver` uses `configScope` annotations to determine which fields are hot-reloadable. Extending to connector fields is the same pattern | +| `RuntimeConfigResolver` two-layer model handles connector config | **YES** | The `RuntimeConfigResolver` already implements YAML baseline + DB overrides with 30s TTL. Extending it to connector settings means adding connector config keys to the resolver's scope. No framework changes — this is extending boost's own infrastructure | +| Changes take effect within 30s TTL without pod restart | **YES** | `RuntimeConfigResolver` already delivers this for existing config. Connector code reads config through the resolver instead of directly from `ConfigApi`, getting hot-reload for free | +| Admin UI section for connector configuration | **YES** | Frontend page in the boost admin panel, writing to the DB override layer via the existing admin API. Same pattern as existing admin sections | + +**Verdict: FULLY FEASIBLE** — This epic extends boost's proven `RuntimeConfigResolver` to connector settings. The resolver already handles hot-reload with 30s TTL; connector config is just more fields under its scope. No upstream Backstage changes needed. + +**Key distinction from Backstage native config:** Backstage's built-in `ConfigApi` loads app-config at startup and does not support hot-reload. Boost's `RuntimeConfigResolver` is a custom layer on top that adds DB overrides and TTL-based refresh. This epic extends that custom layer, not the Backstage config system. + +**Note on credential references:** Hot-reloading credential references (K8s Secret mounts) has a subtlety — the Secret value is read from the filesystem, not from app-config. If a Secret is rotated (the mounted file changes), the connector must re-read it. The 30s TTL should trigger a re-read of the mounted file, but this needs testing with K8s Secret mount propagation delays (which can take up to 60s for projected volumes). + +--- + +### RHIDP-15333: Ingestion Audit Logging and Metrics + +**Summary:** Audit log every sync attempt and config change. Expose analytics metrics (sync history, quality scores, match coverage, Neo4j sync status) via RBAC-gated REST API. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| ------------------------------------------------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Every sync attempt logged with connector name, timestamp, outcome, asset counts | **YES** | Application-level structured logging. Connectors emit a structured audit event at sync completion. RHIDP-15277 (RHDHPLAN-1508) established the pattern — same approach for ingestion events. Write to the RHDH audit log channel | +| Every config change logged with actor, timestamp, before/after values | **YES** | Application-level — the admin API endpoint that writes config changes emits an audit event with the actor (from request identity), old value, and new value | +| Analytics metrics exposed via RBAC-gated REST API | **YES** | Backend REST API route gated by permission check (e.g., `ai-catalog.admin`). Returns aggregated metrics from stored sync history and quality data. Standard Backstage backend plugin pattern | +| Eval Hub integration for skill quality scores | **YES with caveats** | Application-level integration — REST calls to LightEval, IBM Clear, GuideLLM APIs. The eval hub APIs are external dependencies; availability and API stability are the risk, not Backstage framework limitations | + +**Verdict: FULLY FEASIBLE** — Audit logging and metrics are application-level concerns. RHIDP-15277 (RHDHPLAN-1508) already establishes the audit logging pattern; this epic extends it to ingestion events. The analytics API is a standard RBAC-gated REST endpoint. No upstream changes needed. + +**Eval Hub caveat:** The acceptance criterion mentions "Eval Hub integration for skill quality scores" using LightEval, IBM Clear, and GuideLLM. These are external evaluation frameworks with their own APIs. The feasibility of this criterion depends on those APIs being stable and accessible, not on Backstage capabilities. If these APIs are not yet available or their integration contracts are not defined, this criterion may need to be scoped down to "quality score ingestion from a configurable source" rather than specific eval framework integrations. + +--- + +### RHIDP-15334: Upstream Schema Alignment Readiness + +**Summary:** Document RHDH AI Asset annotation/versioning scheme with explicit mapping to draft RFCs #32062 and #33060. Build dry-run migration-readiness tooling scaffold. + +#### Acceptance Criteria Assessment + +| Criterion | Feasible without upstream changes? | Assessment | +| --------------------------------------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Annotation specification document with explicit RFC mapping published | **YES** | Documentation exercise. Map each `rhdh.io/ai-asset-*` annotation and `spec.type` value to the corresponding proposed upstream RFC kind. This is analysis and documentation — no code changes to the framework | +| Dry-run migration-readiness tooling scaffold implemented | **YES** | A CLI tool or script that: (1) queries the catalog API for all entities with `rhdh.io/ai-asset-category` annotation, (2) for each entity, computes what upstream kind it would become under RFCs #32062 / #33060, (3) reports per-entity: current kind/type → proposed upstream kind, fields that need transformation, and any incompatibilities. Standard catalog API query + mapping logic | +| Actual migration explicitly framed as future work | **YES** | Documentation/scoping statement — no implementation needed | + +**Verdict: FULLY FEASIBLE** — This is the most straightforward epic. It's primarily documentation and a dry-run analysis tool. The catalog API provides entity enumeration; the mapping logic is a static table based on the current annotation scheme and the RFC proposals. No upstream changes needed — and by design, this epic explicitly defers the actual migration. + +**Key consideration:** The utility of this epic depends on the upstream RFCs' stability. If RFCs #32062 and #33060 are still in early draft with significant open questions, the mapping document will need to capture uncertainty ("if the RFC adopts option A, the mapping is X; if option B, the mapping is Y"). The dry-run tool should report confidence levels per entity based on how settled the target RFC kind is. + +--- + +## Summary Matrix + +| Epic | Key | Feasible without upstream changes? | Implementation complexity | Notes | +| ----------------------------------- | ----------- | ---------------------------------- | ------------------------- | -------------------------------------------------------------------------------- | +| Ingestion Health Admin Dashboard | RHIDP-15331 | **YES** | Medium | Standard admin page + backend API; boost admin panel already has this pattern | +| Connector Configuration Hot-Reload | RHIDP-15332 | **YES** | Medium | Extends proven `RuntimeConfigResolver`; K8s Secret rotation timing needs testing | +| Ingestion Audit Logging and Metrics | RHIDP-15333 | **YES** | Medium | Extends RHIDP-15277 audit pattern; Eval Hub API availability is the main risk | +| Upstream Schema Alignment Readiness | RHIDP-15334 | **YES** | Low | Documentation + dry-run tool; depends on RFC stability | + +## Key Findings + +1. **All 4 epics are fully feasible without upstream Backstage changes.** RHDHPLAN-1513 is the most internally focused of the four features — three of the four epics build on boost's own infrastructure (`RuntimeConfigResolver`, admin panel, audit logging) rather than Backstage extension points. + +2. **No PM discussion needed.** All acceptance criteria can be implemented as specified. No spec deviations required. + +3. **Boost's `RuntimeConfigResolver` is the key enabler for RHIDP-15332.** Hot-reload config is not a native Backstage capability — boost built it. Extending it to connector settings is a natural progression of that investment. + +4. **The audit logging pattern is now a cross-cutting concern.** RHIDP-15277 (RHDHPLAN-1508) establishes the pattern for RBAC audit events; RHIDP-15333 (this feature) extends it to ingestion events. These should share the same audit event infrastructure (structured JSON, dedicated log channel, consistent event schema) to avoid parallel implementations. + +5. **Two external dependency risks (neither is a Backstage framework issue):** + - **Eval Hub APIs** (LightEval, IBM Clear, GuideLLM) — API availability and contract stability are unknowns. If these APIs are not ready, the quality scores criterion should be scoped to a generic "quality score ingestion" interface. + - **Upstream RFCs #32062 and #33060** — if these RFCs undergo significant revision, the mapping document and dry-run tool will need updates. The epic correctly frames actual migration as future work. + +## Comparison Across All Four Features + +| Aspect | RHDHPLAN-1507 (Entity Model) | RHDHPLAN-1508 (RBAC) | RHDHPLAN-1510 (Connectors) | RHDHPLAN-1513 (Ops Infra) | +| ------------------------------- | ---------------------------- | ----------------------------- | -------------------------- | --------------------------- | +| Epic count | 7 | 7 | 4 | 4 | +| Epics requiring spec deviations | 0 | 3 | 0 | 0 | +| Upstream changes needed | None | None (3 alternate approaches) | None | None | +| Framework alignment | High | Medium | High | High | +| PM discussion needed | No | Yes (3 areas) | No | No | +| Highest-risk epic | RHIDP-15295 (Neo4j) | RHIDP-15274 (cascade) | RHIDP-15315 (OCI scale) | RHIDP-15333 (Eval Hub APIs) | +| Primary cross-reference | Catalog framework | Permission framework | Entity provider interface | Boost internal infra | + +**Overall:** 22 epics across 4 features. 19 of 22 are fully feasible as specified. 3 (all in RHDHPLAN-1508) require implementation approaches that deviate from the spec text and need PM discussion. Zero require upstream Backstage changes.