Skip to content

feat(#3693): add AIResourceOciProcessor for OCI ingestion validation#3764

Merged
johnmcollier merged 4 commits into
mainfrom
agent/3693-ai-resource-oci-processor
Jul 17, 2026
Merged

feat(#3693): add AIResourceOciProcessor for OCI ingestion validation#3764
johnmcollier merged 4 commits into
mainfrom
agent/3693-ai-resource-oci-processor

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown
Contributor

Implement AIResourceOciProcessor as a CatalogProcessor that validates OCI-backed AIResource entities during catalog ingestion. The processor checks that spec.location.target uses the oci:// URI scheme when spec.location.type is oci, and rejects malformed targets with actionable error messages including the field path, received value, and expected format.

The processor makes zero outbound HTTP or registry network calls — validation is format-only, keeping ingestion stateless and air-gap safe per design decision D4.

Changes:

  • AIResourceOciProcessor.ts: new processor with oci:// prefix,
    non-empty, and registry/repository path validation
  • module.ts: register AIResourceOciProcessor alongside the existing
    AIResourceExtensionsProcessor
  • index.ts: export AIResourceOciProcessor for public API
  • AIResourceOciProcessor.test.ts: 22 tests covering valid targets,
    malformed targets, non-OCI passthrough, non-AIResource passthrough,
    zero-network behavior, and error quality

Assisted-by: Claude


Closes #3693

Post-script verification

  • Branch is not main/master (agent/3693-ai-resource-oci-processor)
  • Secret scan passed (gitleaks — 6e585f281bb664af5c79c8b717c8b3a949c247ea..HEAD)
  • Pre-commit hooks passed (authoritative run on runner)
  • Tests ran inside sandbox

Implement AIResourceOciProcessor as a CatalogProcessor that validates
OCI-backed AIResource entities during catalog ingestion. The processor
checks that spec.location.target uses the oci:// URI scheme when
spec.location.type is oci, and rejects malformed targets with
actionable error messages including the field path, received value,
and expected format.

The processor makes zero outbound HTTP or registry network calls —
validation is format-only, keeping ingestion stateless and air-gap
safe per design decision D4.

Changes:
- AIResourceOciProcessor.ts: new processor with oci:// prefix,
  non-empty, and registry/repository path validation
- module.ts: register AIResourceOciProcessor alongside the existing
  AIResourceExtensionsProcessor
- index.ts: export AIResourceOciProcessor for public API
- AIResourceOciProcessor.test.ts: 22 tests covering valid targets,
  malformed targets, non-OCI passthrough, non-AIResource passthrough,
  zero-network behavior, and error quality

Closes #3693

Assisted-by: Claude
@rhdh-gh-app

rhdh-gh-app Bot commented Jul 15, 2026

Copy link
Copy Markdown

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-catalog-backend-module-catalog-backend-module-ai-resource-extensions workspaces/ai-integrations/plugins/catalog-backend-module-catalog-backend-module-ai-resource-extensions minor v0.2.0

@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.42857% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 54.70%. Comparing base (6e585f2) to head (7783fdc).
⚠️ Report is 34 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3764      +/-   ##
==========================================
+ Coverage   54.68%   54.70%   +0.01%     
==========================================
  Files        2366     2367       +1     
  Lines       90284    90312      +28     
  Branches    25270    25278       +8     
==========================================
+ Hits        49374    49401      +27     
+ Misses      40621    40612       -9     
- Partials      289      299      +10     
Flag Coverage Δ *Carryforward flag
adoption-insights 83.70% <ø> (ø) Carriedforward from c4343f9
ai-integrations 69.00% <96.42%> (+1.07%) ⬆️
app-defaults 69.79% <ø> (ø) Carriedforward from c4343f9
augment 46.39% <ø> (ø) Carriedforward from c4343f9
boost 73.13% <ø> (ø) Carriedforward from c4343f9
bulk-import 72.46% <ø> (ø) Carriedforward from c4343f9
cost-management 14.10% <ø> (ø) Carriedforward from c4343f9
dcm 61.81% <ø> (ø) Carriedforward from c4343f9
extensions 61.53% <ø> (ø) Carriedforward from c4343f9
global-floating-action-button 71.18% <ø> (ø) Carriedforward from c4343f9
global-header 59.71% <ø> (ø) Carriedforward from c4343f9
homepage 50.30% <ø> (ø) Carriedforward from c4343f9
install-dynamic-plugins 56.77% <ø> (ø) Carriedforward from c4343f9
konflux 91.49% <ø> (ø) Carriedforward from c4343f9
lightspeed 69.02% <ø> (ø) Carriedforward from c4343f9
mcp-integrations 85.46% <ø> (ø) Carriedforward from c4343f9
orchestrator 43.84% <ø> (ø) Carriedforward from c4343f9
quickstart 65.63% <ø> (ø) Carriedforward from c4343f9
sandbox 79.56% <ø> (ø) Carriedforward from c4343f9
scorecard 82.96% <ø> (ø) Carriedforward from c4343f9
theme 61.26% <ø> (ø) Carriedforward from c4343f9
translations 7.25% <ø> (ø) Carriedforward from c4343f9
x2a 78.68% <ø> (ø) Carriedforward from c4343f9

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6e585f2...7783fdc. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@johnmcollier

Copy link
Copy Markdown
Member

/fs-fix address failing CI checks, also ensure API exports updated

@fullsend-ai-coder

fullsend-ai-coder Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Fix · ❌ Terminated · Started 10:17 PM UTC · Ended 10:31 PM UTC
Commit: a25227a · View workflow run →

Regenerate report.api.md to include the new AIResourceOciProcessor
class in the public API surface. This fixes the CI "check api reports"
step that was failing because the export was added to index.ts but the
API report was not updated.

Addresses review feedback on #3764

Assisted-by: Claude
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 1 (human-triggered)

Fixed failing CI by regenerating the API report (report.api.md) to include the new AIResourceOciProcessor export. The CI 'check api reports' step was failing because the new processor class was added to index.ts but the API report was not updated. All 41 tests pass.

Fixed (2):

  1. CI check 'check api reports and generate API reference' failing (workspaces/ai-integrations/plugins/catalog-backend-module-catalog-backend-module-ai-resource-extensions/report.api.md): Regenerated report.api.md to include the new AIResourceOciProcessor class export. The CI was failing because the new processor was exported from index.ts but the API report file was not updated to reflect the new public API surface.
  2. Ensure API exports updated (workspaces/ai-integrations/plugins/catalog-backend-module-catalog-backend-module-ai-resource-extensions/report.api.md): Verified that the AIResourceOciProcessor class is correctly exported in index.ts and now properly reflected in report.api.md with its full public API signature (getProcessorName and preProcessEntity methods).

Tests: passed

Updated by fullsend fix agent

@johnmcollier

Copy link
Copy Markdown
Member

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 16, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:39 PM UTC · Completed 2:44 PM UTC
Commit: 85f13de · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review — comment

PR #3764 adds AIResourceOciProcessor, a new CatalogProcessor that validates OCI-backed AIResource entities during catalog ingestion. The implementation is well-scoped to issue #3693 and aligns with design decision D4 (format-only validation, zero network calls, air-gap safe). The processor is correctly registered, exported, and tested with 22 test cases covering valid targets, malformed inputs, non-OCI passthrough, and zero-network behavior.

Findings

1. Multi-error reporting trade-off · medium · correctness

File: AIResourceOciProcessor.ts

The original JSDoc on AIResourceExtensionsProcessor stated: "Additional extension validators (for example OCI location rules) should be added here so errors can be reported together." The ingestion spec (spec.md) also includes a requirement: "WHEN an AIResource entity has multiple invalid fields THEN all validation errors are reported in a single response."

By splitting OCI validation into a separate processor, an entity with both an invalid spec.scope and a malformed OCI target will produce errors across two separate processing cycles rather than in a single consolidated response. The first processor to throw aborts the pipeline for that entity; the user fixes one error, retries, and then sees the next.

This is an architectural trade-off, not a bug — design decision D4 explicitly calls for "a dedicated processor" and the issue scopes this as a separate class. However, the replacement JSDoc comment should document why the separate-processor approach was chosen over co-location, rather than simply redirecting the reader.

Suggested improvement: Update the JSDoc to briefly note the rationale — e.g., "OCI location validation is handled by the dedicated {@link AIResourceOciProcessor} (design decision D4). The processors validate independent concerns and run in the same module."


2. Trailing-slash OCI reference accepted · low · correctness

File: AIResourceOciProcessor.ts (line 51)

A target like oci://registry/repo/ passes validation because parts = ['registry', 'repo', ''] has parts[0] and parts[1] non-empty. The empty trailing segment is not checked. While the spec says "format validation only," silently accepting empty path segments may cause confusion downstream.

Remediation: Consider adding parts.some(p => p === '') as an additional rejection condition, or document that trailing slashes are intentionally permitted.


3. Leading whitespace produces misleading error · low · correctness

File: AIResourceOciProcessor.ts (line 30)

A target like " oci://quay.io/org/model:tag" (leading space) passes the trim() non-empty check but fails startsWith('oci://'), producing a "must start with oci:// prefix" error. The actual issue is extraneous whitespace, which is a common copy-paste artifact. A trim-then-validate approach or a specific whitespace diagnostic would improve the user experience.


4. Unicode bidi characters pass sanitization filter · low · security

File: AIResourceOciProcessor.ts

The sanitization filter .filter(c => c.charCodeAt(0) > 0x1f) removes C0 control characters but allows Unicode bidirectional overrides (U+202A–U+202E), zero-width characters, and other format characters. A crafted target could render misleadingly in log viewers. This is rated low because: (a) the value only appears in thrown Error messages in processing logs, not user-facing HTML; (b) the same pattern already exists in the sibling AIResourceExtensionsProcessor.ts — this is not a regression introduced by this PR.


5. Plugin README does not document new public export · low · docs-currency

File: README.md

The plugin README documents only AIResourceExtensionsProcessor and its three public exports. The new AIResourceOciProcessor is a @public export but is not mentioned in the README or its Public API table. While broad documentation is listed as out-of-scope in issue #3693, the plugin's own README listing its public API surface should reflect the new export.


6. Missing changeset for version bump · low · cross-repo-contracts

File: package.json

The package is at version 0.2.0. Adding a new public export and auto-registering a new processor constitutes a minor feature addition. No changeset file is included in the PR to trigger a version bump. Under semver, this should result in a 0.3.0 bump.


Assessment

The implementation is clean, well-tested, and aligned with the design documents and issue scope. The medium finding is an architectural trade-off worth documenting but does not warrant blocking the PR — the design decision D4 and issue #3693 both support the separate-processor approach. All low findings are minor edge cases or documentation improvements suitable for follow-up.

No security vulnerabilities, no test integrity concerns, no scope creep, no breaking API changes.


Labels: PR adds a new backend catalog processor feature in the ai-integrations workspace.

Previous run

Review of PR #3764AIResourceOciProcessor for OCI ingestion validation

Verdict: approve

This PR adds a CatalogProcessor that validates OCI-backed AIResource entities during catalog ingestion. The implementation is clean, well-scoped to issue #3693 and section 2 of the task breakdown, and adheres to design decision D4 (format-only validation, zero outbound network calls).

What was reviewed

Dimension Assessment
Correctness ✅ Logic is sound; validation checks are sequentially dependent and ordered correctly
Security ✅ Input is sanitized in error messages (control char stripping, 200-char truncation); zero network surface
Intent & coherence ✅ Change matches issue #3693 scope exactly; no scope creep
Style/conventions ✅ Follows patterns established by AIResourceExtensionsProcessor
Documentation ✅ API report updated; JSDoc present on the public class
Cross-repo contracts ✅ New public export is additive only; no breaking changes

Observations

Strengths:

  • Comprehensive test suite (22 tests) covering valid targets, malformed targets, non-OCI passthrough, non-AIResource passthrough, zero-network verification, and error quality.
  • Error messages include field path (spec.location.target), the received value, and expected format — meeting the spec's actionable-error requirement.
  • Sanitization of user-controlled values in error messages mirrors the existing processor's approach.
  • Module registration is correct — the new processor runs alongside AIResourceExtensionsProcessor.

Findings

1. OCI URI validation allows degenerate edge cases — low

File: AIResourceOciProcessor.ts (line 80 in diff)

The path check ociPath.length === 0 || !ociPath.includes('/') only verifies the path is non-empty and contains at least one /. This allows semantically degenerate URIs:

  • oci:///foo passes (no registry host — ociPath is /foo)
  • oci://host/ passes (empty repository — ociPath is host/)

These are unlikely in practice, and the validation is intentionally lightweight per design D4. A future tightening (e.g., requiring at least one non-empty segment on each side of the first /) would close these gaps without adding real complexity.

2. Misleading variable name locationTypelow

File: AIResourceOciProcessor.ts (line 54 in diff)

const locationType = (entity.spec as Record<string, unknown> | undefined)
  ?.location as Record<string, unknown> | undefined;

locationType actually holds the entire spec.location object, not the location type string. A name like specLocation or locationObj would be clearer and prevent future confusion.

3. Stale code comment in existing processor — low

File: AIResourceExtensionsProcessor.ts (line 51, not in this diff)

The existing processor's JSDoc says "Additional extension validators (for example OCI location rules) should be added here so errors can be reported together." This comment is now stale since OCI validation lives in a separate processor. Updating it would prevent confusion for future contributors.


All findings are low-severity follow-up suggestions. The implementation is correct, well-tested, and ready to merge.

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the ready-for-merge All reviewers approved — ready to merge label Jul 16, 2026
@johnmcollier

Copy link
Copy Markdown
Member

/fs-fix address review comments

@fullsend-ai-coder

fullsend-ai-coder Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Finished Fix · ✅ Success · Started 7:01 PM UTC · Completed 7:13 PM UTC
Commit: f71bd18 · View workflow run →

- Tighten OCI URI validation to reject degenerate edge cases like
  oci:///foo (no registry) and oci://host/ (empty repository) by
  splitting the path and verifying both registry and repository
  segments are non-empty
- Rename misleading variable 'locationType' to 'specLocation' since
  it holds the entire spec.location object, not the type string
- Update stale JSDoc in AIResourceExtensionsProcessor that incorrectly
  suggested OCI rules should be added there
- Add test cases for the newly rejected degenerate URIs

Addresses review feedback on #3764

Assisted-by: Claude
@fullsend-ai-coder

Copy link
Copy Markdown
Contributor Author

🔧 Fix agent — iteration 2 (human-triggered)

Addressed all 3 review findings: tightened OCI URI validation to reject degenerate edge cases, renamed misleading variable, and updated stale JSDoc comment. Added 2 new test cases. All 43 tests pass.

Fixed (3):

  1. OCI URI validation allows degenerate edge cases (workspaces/ai-integrations/plugins/catalog-backend-module-catalog-backend-module-ai-resource-extensions/src/AIResourceOciProcessor.ts): Tightened OCI path validation by splitting on '/' and verifying both registry (parts[0]) and repository (parts[1]) segments are non-empty. This rejects degenerate URIs like oci:///foo (no registry) and oci://host/ (empty repository) that previously passed. Added two corresponding test cases.
  2. Misleading variable name locationType (workspaces/ai-integrations/plugins/catalog-backend-module-catalog-backend-module-ai-resource-extensions/src/AIResourceOciProcessor.ts): Renamed variable 'locationType' to 'specLocation' since it holds the entire spec.location object, not the location type string.
  3. Stale code comment in existing processor (workspaces/ai-integrations/plugins/catalog-backend-module-catalog-backend-module-ai-resource-extensions/src/AIResourceExtensionsProcessor.ts): Updated JSDoc that incorrectly suggested OCI location rules should be added to AIResourceExtensionsProcessor. Now correctly references the separate AIResourceOciProcessor.

Tests: passed

Updated by fullsend fix agent

@johnmcollier

Copy link
Copy Markdown
Member

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 16, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:49 PM UTC · Completed 8:02 PM UTC
Commit: f71bd18 · 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.

@fullsend-ai-review fullsend-ai-review Bot removed the ready-for-merge All reviewers approved — ready to merge label Jul 16, 2026
@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment feature labels Jul 16, 2026
Reject trailing-slash and whitespace edge cases in OCI targets, document
AIResourceOciProcessor in the plugin README, and add a minor changeset.

Addresses review feedback on #3764

Assisted-by: Cursor Grok 4.5
Co-authored-by: Cursor <cursoragent@cursor.com>
@sonarqubecloud

Copy link
Copy Markdown

@gabemontero gabemontero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

had no other comments other than my take on the fullsend review comments @johnmcollier

@johnmcollier
johnmcollier merged commit 8317e6e into main Jul 17, 2026
72 checks passed
@fullsend-ai-retro

fullsend-ai-retro Bot commented Jul 17, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 5:02 PM UTC · Completed 5:14 PM UTC
Commit: 8317e6e · View workflow run →

@fullsend-ai-retro

Copy link
Copy Markdown

PR #3764 added AIResourceOciProcessor for OCI ingestion validation in the ai-integrations workspace. The code agent produced a clean initial implementation with 22 tests, but missed the API report regeneration step (already tracked in #3757), causing one avoidable fix iteration. The review agent delivered high-quality findings across 2 review rounds (8 unique issues), but the fix agent's validation tightening was incomplete — it missed trailing-slash and whitespace edge cases of the same pattern it modified. The first review submitted as APPROVED despite its body stating a 'comment' verdict (additional evidence for fullsend-ai/fullsend#4585). A human reviewer agreed with all agent findings and raised no novel issues, suggesting strong autonomy readiness for this change class. The PR merged in ~2 days with 4 commits.

Proposals filed

Evidence notes (not filed as issues)

  • Additional evidence for review verdict mismatch: body says 'comment' but GitHub state is APPROVED (fullsend-ai/fullsend): On PR feat(#3693): add AIResourceOciProcessor for OCI ingestion validation #3764 (feat(#3693): add AIResourceOciProcessor for OCI ingestion validation #3764), the human triggered /fs-review at 2026-07-16T14:38 UTC. The review agent (run 29507493558) completed at 14:44 UTC and posted a sticky comment with header '## Review — comment' containing 6 findings (1 medium architectural concern about multi-error reporting, 5 low correctness/style/docs findings). The assessment stated the medium finding 'does not warrant blocking the PR' and low findings are 'suitable for follow-up.' Despite the 'comment' verdict in the body text, the GitHub review API shows state=APPROVED at 2026-07-16T14:44:41Z. The sticky comment also contained a 'Previous run' section from an earlier auto-review that explicitly stated 'Verdict: approve'. The second manual review on the same PR (run 29529478533, 2026-07-16T20:02:27Z) correctly submitted as COMMENTED with the same body format.

JslYoon pushed a commit to JslYoon/rhdh-plugins that referenced this pull request Jul 20, 2026
…tion validation (redhat-developer#3764)

* feat(redhat-developer#3693): add AIResourceOciProcessor for OCI ingestion validation

Implement AIResourceOciProcessor as a CatalogProcessor that validates
OCI-backed AIResource entities during catalog ingestion. The processor
checks that spec.location.target uses the oci:// URI scheme when
spec.location.type is oci, and rejects malformed targets with
actionable error messages including the field path, received value,
and expected format.

The processor makes zero outbound HTTP or registry network calls —
validation is format-only, keeping ingestion stateless and air-gap
safe per design decision D4.

Changes:
- AIResourceOciProcessor.ts: new processor with oci:// prefix,
  non-empty, and registry/repository path validation
- module.ts: register AIResourceOciProcessor alongside the existing
  AIResourceExtensionsProcessor
- index.ts: export AIResourceOciProcessor for public API
- AIResourceOciProcessor.test.ts: 22 tests covering valid targets,
  malformed targets, non-OCI passthrough, non-AIResource passthrough,
  zero-network behavior, and error quality

Closes redhat-developer#3693

Assisted-by: Claude

* fix(redhat-developer#3693): update API report for AIResourceOciProcessor export

Regenerate report.api.md to include the new AIResourceOciProcessor
class in the public API surface. This fixes the CI "check api reports"
step that was failing because the export was added to index.ts but the
API report was not updated.

Addresses review feedback on redhat-developer#3764

Assisted-by: Claude

* fix: address review feedback on OCI processor validation

- Tighten OCI URI validation to reject degenerate edge cases like
  oci:///foo (no registry) and oci://host/ (empty repository) by
  splitting the path and verifying both registry and repository
  segments are non-empty
- Rename misleading variable 'locationType' to 'specLocation' since
  it holds the entire spec.location object, not the type string
- Update stale JSDoc in AIResourceExtensionsProcessor that incorrectly
  suggested OCI rules should be added there
- Add test cases for the newly rejected degenerate URIs

Addresses review feedback on redhat-developer#3764

Assisted-by: Claude

* fix: tighten OCI URI validation and address review feedback

Reject trailing-slash and whitespace edge cases in OCI targets, document
AIResourceOciProcessor in the plugin README, and add a minor changeset.

Addresses review feedback on redhat-developer#3764

Assisted-by: Cursor Grok 4.5
Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: fullsend-code <278716306+fullsend-ai-coder[bot]@users.noreply.github.com>
Co-authored-by: John Collier <jcollier@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature ready-for-review Agent PR ready for human review requires-manual-review Review requires human judgment workspace/ai-integrations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ai-integrations: implement AIResourceOciProcessor for OCI ingestion

2 participants