Skip to content

feat: ability to match agents to specific resources#963

Merged
adityachoudhari26 merged 3 commits intomainfrom
agent-resource-matchers
Apr 9, 2026
Merged

feat: ability to match agents to specific resources#963
adityachoudhari26 merged 3 commits intomainfrom
agent-resource-matchers

Conversation

@adityachoudhari26
Copy link
Copy Markdown
Member

@adityachoudhari26 adityachoudhari26 commented Apr 9, 2026

Resolves #962

Summary by CodeRabbit

  • New Features
    • Job agent selectors can now reference resource configuration and metadata fields (e.g., resource.config.*, resource.metadata.*) for more sophisticated agent matching logic.
    • Agent matching is now performed per-target resource, enabling targeted job assignments based on individual resource properties.

Copilot AI review requested due to automatic review settings April 9, 2026 22:47
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

Warning

Rate limit exceeded

@adityachoudhari26 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 25 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 9 minutes and 25 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0b8bb915-83be-4e64-bdca-b87770ae430f

📥 Commits

Reviewing files that changed from the base of the PR and between c747e79 and 60e60af.

📒 Files selected for processing (7)
  • apps/workspace-engine/pkg/selector/jobagents.go
  • apps/workspace-engine/svc/controllers/deploymentplan/controller.go
  • apps/workspace-engine/svc/controllers/jobdispatch/reconcile.go
  • apps/workspace-engine/svc/controllers/jobdispatch/reconcile_test.go
  • apps/workspace-engine/svc/controllers/jobeligibility/reconcile.go
  • apps/workspace-engine/svc/http/server/openapi/deployments/server.go
  • apps/workspace-engine/test/controllers/harness/mocks.go
📝 Walkthrough

Walkthrough

This PR introduces resource-aware job-agent selectors by extending the CEL evaluation environment to expose resource fields, creating a new MatchJobAgentsWithResource function, and updating multiple controllers to fetch and pass resources during selector evaluation.

Changes

Cohort / File(s) Summary
Selector Core
apps/workspace-engine/pkg/selector/jobagents.go
Added jobAgentWithResourceEnv CEL environment that exposes jobAgent and resource map variables with a resourceToMap converter. Created new MatchJobAgentsWithResource function for resource-aware matching and refactored logic into shared matchJobAgents helper; MatchJobAgents now ignores its context parameter.
Getter Interface & Implementation
apps/workspace-engine/svc/controllers/jobdispatch/getters.go, getters_postgres.go
Extended Getter interface with GetResource(ctx, resourceID) method; implemented in PostgresGetter to fetch resources by ID from database and convert via db.ToOapiResource.
Deployment Plan Controller
apps/workspace-engine/svc/controllers/deploymentplan/controller.go
Removed plan-level job-agent selector filtering and moved per-resource matching into processTarget using MatchJobAgentsWithResource; early return when match result is empty.
Job Dispatch Reconciliation
apps/workspace-engine/svc/controllers/jobdispatch/reconcile.go
Updated getJobAgents to fetch resource via getter and pass it to MatchJobAgentsWithResource instead of MatchJobAgents.
Job Eligibility Reconciliation
apps/workspace-engine/svc/controllers/jobeligibility/reconcile.go, reconcile_test.go
Updated buildAndDispatchJob to fetch resource and use MatchJobAgentsWithResource; added comprehensive test suite (Reconcile_ResourceAwareSelector_*) covering resource field references in selectors (resource.config.*, resource.metadata.*), error handling, and combined predicates.
Test Infrastructure
apps/workspace-engine/svc/controllers/jobdispatch/reconcile_test.go, apps/workspace-engine/test/controllers/harness/mocks.go
Extended mockGetter and JobDispatchGetter test doubles with GetResource method returning default oapi.Resource with empty Config and Metadata.

Sequence Diagram

sequenceDiagram
    participant Controller as Controller
    participant Getter as Getter (DB)
    participant Selector as Selector
    participant CEL as CEL Engine

    Controller->>Getter: GetResource(resourceID)
    Getter-->>Controller: *oapi.Resource
    
    Controller->>Selector: MatchJobAgentsWithResource(selector, agents, resource)
    Selector->>Selector: Compile selector expression
    Selector->>CEL: Evaluate with jobAgent & resource variables
    CEL-->>Selector: matched agents
    Selector-->>Controller: []oapi.JobAgent
    
    Controller->>Controller: Process matched agents
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Resource-wise now, our selectors grow,
With CEL maps that shimmer and glow,
Each controller knows what agents fit best,
From config to metadata—put them to the test!
No more blind matching—resources shine through,
Smart job dispatch, forever anew! 🎯

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: ability to match agents to specific resources' directly and accurately summarizes the main change: extending agent matching functionality to consider individual resources.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent-resource-matchers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds support for resource-aware job-agent selection so job agents can be matched based on properties of the target resource (config/metadata) in addition to job-agent fields.

Changes:

  • Extend CEL selector evaluation to include a resource variable alongside jobAgent.
  • Update job eligibility, job dispatch, and deployment plan flows to fetch the target resource and evaluate selectors with resource context.
  • Add/adjust mocks and tests to cover resource-aware selector behavior and GetResource plumbing.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
apps/workspace-engine/pkg/selector/jobagents.go Adds MatchJobAgentsWithResource and resource mapping for CEL selector evaluation.
apps/workspace-engine/svc/controllers/jobeligibility/reconcile.go Fetches resource and evaluates selectors using resource-aware matching before creating jobs.
apps/workspace-engine/svc/controllers/jobeligibility/reconcile_test.go Adds test coverage for selectors referencing resource.config and resource.metadata.
apps/workspace-engine/svc/controllers/jobdispatch/getters.go Extends Getter interface to include GetResource.
apps/workspace-engine/svc/controllers/jobdispatch/getters_postgres.go Implements GetResource in the Postgres getter.
apps/workspace-engine/svc/controllers/jobdispatch/reconcile.go Fetches resource from release target and evaluates selectors with resource-aware matching.
apps/workspace-engine/svc/controllers/jobdispatch/reconcile_test.go Updates controller test getter mock to support GetResource.
apps/workspace-engine/svc/controllers/deploymentplan/controller.go Moves selector matching into per-target processing using resource-aware matching.
apps/workspace-engine/test/controllers/harness/mocks.go Updates harness mock to implement GetResource.
Comments suppressed due to low confidence (1)

apps/workspace-engine/svc/controllers/deploymentplan/controller.go:87

  • Deployment plans are no longer completed when there are job agents in the workspace but none match the selector for any release target. Process only calls CompletePlan when len(allAgents)==0, and processTarget just returns nil when len(matchedAgents)==0, so the plan can remain stuck with no enqueued results. Track whether any target/agent result was enqueued and call CompletePlan at the end when none were, or reintroduce a plan-level fast-path when the selector matches zero agents across all targets.
	allAgents, err := c.getter.ListJobAgentsByWorkspaceID(ctx, plan.WorkspaceID)
	if err != nil {
		return reconcile.Result{}, fmt.Errorf("list job agents: %w", err)
	}

	if len(allAgents) == 0 {
		if err := c.setter.CompletePlan(ctx, planID); err != nil {
			return reconcile.Result{}, fmt.Errorf("mark plan completed: %w", err)
		}
		return reconcile.Result{}, nil
	}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 106 to +111
vars := map[string]any{
"jobAgent": jobAgentToMap(&agents[i]),
}
if resource != nil {
vars["resource"] = resourceToMap(resource)
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

resourceToMap(resource) is recomputed for every agent during selector evaluation, allocating and populating the same map repeatedly. Precompute the mapped resource once (outside the agent loop) and reuse it in vars to reduce allocations and CPU when evaluating selectors against many agents.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/workspace-engine/pkg/selector/jobagents.go (2)

76-83: Make resource contract explicit in MatchJobAgentsWithResource

On Line 80, resource is pointer-typed but treated as optional implicitly. Returning a clear early error for nil avoids ambiguous per-agent CEL evaluation failures later.

Proposed change
 func MatchJobAgentsWithResource(
 	_ context.Context,
 	selector string,
 	agents []oapi.JobAgent,
 	resource *oapi.Resource,
 ) ([]oapi.JobAgent, error) {
+	if resource == nil {
+		return nil, fmt.Errorf("match job agents with resource: resource is required")
+	}
 	return matchJobAgents(jobAgentWithResourceEnv, selector, agents, resource)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workspace-engine/pkg/selector/jobagents.go` around lines 76 - 83, Make
the resource pointer contract explicit in MatchJobAgentsWithResource: detect if
the incoming resource argument is nil at the start of MatchJobAgentsWithResource
and return a clear error instead of proceeding; this prevents ambiguous
per-agent CEL evaluation failures later in matchJobAgents and in
jobAgentWithResourceEnv which expect a non-nil resource. Ensure the returned
error message clearly states that resource is required (e.g., "resource
required") and update callers/tests as needed.

109-111: Avoid recomputing resourceToMap for every agent

Line 110 rebuilds the same resource map on each iteration. Compute it once before the loop and reuse it.

Proposed change
 	var matched []oapi.JobAgent
+	var resourceVars map[string]any
+	if resource != nil {
+		resourceVars = resourceToMap(resource)
+	}
 	for i := range agents {
 		vars := map[string]any{
 			"jobAgent": jobAgentToMap(&agents[i]),
 		}
 		if resource != nil {
-			vars["resource"] = resourceToMap(resource)
+			vars["resource"] = resourceVars
 		}
 		ok, err := celutil.EvalBool(prg, vars)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workspace-engine/pkg/selector/jobagents.go` around lines 109 - 111, The
code currently calls resourceToMap(resource) inside the per-agent loop,
rebuilding the same map for every agent; compute the map once before the loop
(e.g., resourceMap := resourceToMap(resource) when resource != nil) and then set
vars["resource"] = resourceMap inside the loop, reusing that single map instead
of recomputing it each iteration (reference: resourceToMap, vars, resource).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/workspace-engine/pkg/selector/jobagents.go`:
- Around line 76-83: Make the resource pointer contract explicit in
MatchJobAgentsWithResource: detect if the incoming resource argument is nil at
the start of MatchJobAgentsWithResource and return a clear error instead of
proceeding; this prevents ambiguous per-agent CEL evaluation failures later in
matchJobAgents and in jobAgentWithResourceEnv which expect a non-nil resource.
Ensure the returned error message clearly states that resource is required
(e.g., "resource required") and update callers/tests as needed.
- Around line 109-111: The code currently calls resourceToMap(resource) inside
the per-agent loop, rebuilding the same map for every agent; compute the map
once before the loop (e.g., resourceMap := resourceToMap(resource) when resource
!= nil) and then set vars["resource"] = resourceMap inside the loop, reusing
that single map instead of recomputing it each iteration (reference:
resourceToMap, vars, resource).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b86878e0-a9ac-4566-a80a-6622db39d8ce

📥 Commits

Reviewing files that changed from the base of the PR and between 9675cf7 and c747e79.

📒 Files selected for processing (9)
  • apps/workspace-engine/pkg/selector/jobagents.go
  • apps/workspace-engine/svc/controllers/deploymentplan/controller.go
  • apps/workspace-engine/svc/controllers/jobdispatch/getters.go
  • apps/workspace-engine/svc/controllers/jobdispatch/getters_postgres.go
  • apps/workspace-engine/svc/controllers/jobdispatch/reconcile.go
  • apps/workspace-engine/svc/controllers/jobdispatch/reconcile_test.go
  • apps/workspace-engine/svc/controllers/jobeligibility/reconcile.go
  • apps/workspace-engine/svc/controllers/jobeligibility/reconcile_test.go
  • apps/workspace-engine/test/controllers/harness/mocks.go

Comment on lines +64 to +83
func MatchJobAgents(
ctx context.Context,
_ context.Context,
selector string,
agents []oapi.JobAgent,
) ([]oapi.JobAgent, error) {
return matchJobAgents(jobAgentEnv, selector, agents, nil)
}

// MatchJobAgentsWithResource evaluates a CEL job agent selector against a list
// of job agents with the resource available in the CEL context, and returns
// those that match. This allows selectors to reference resource properties,
// e.g. "jobAgent.config.server == resource.config.argocd.serverUrl".
func MatchJobAgentsWithResource(
_ context.Context,
selector string,
agents []oapi.JobAgent,
resource *oapi.Resource,
) ([]oapi.JobAgent, error) {
return matchJobAgents(jobAgentWithResourceEnv, selector, agents, resource)
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

seems weird to wrap this in a function. Also why do we need MatchJobAgents?

@adityachoudhari26 adityachoudhari26 merged commit d05d8bb into main Apr 9, 2026
10 checks passed
@adityachoudhari26 adityachoudhari26 deleted the agent-resource-matchers branch April 9, 2026 23:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: implement resource matching in job dispatch controller

3 participants