You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds adversarial agents - a way to configure agents that check the LLM's output during conversion.
new Adversarial Agents page where you can create, edit, and delete agents.
when creating a project , you can now pick which adversarial agents to run .
the Module page now shows adversarial job output alongside the regular conversion results.
Tested on RHDH 1.10.2 as a dynamic plugin . created agents, selected them in the scaffolder, ran a conversion, and verified adversarial job details appear on the module page.
• Add adversarial agent CRUD APIs, DB storage, and OpenAPI client/server types.
• Allow selecting adversarial agents during scaffolder project creation.
• Run adversarial review jobs and show reports/logs on the Module page.
Diagram
graph TD
UI[["X2A Frontend" انصاف]] --> API(["X2A Backend Router"]) --> DB[("X2A Database")]
API --> KUBE(["KubeService"]) --> CM["Agents ConfigMap"] --> JOB["K8s Job Pod"] --> SCRIPT["x2a-job-script.sh"]
subgraph Legend
direction LR
_ui[["UI Page"]] ~~~ _svc(["Service/Router"]) ~~~ _db[("Database")] ~~~ _file["Runtime artifact"]
end
Loading
High-Level Assessment
The following are alternative approaches to this PR:
1. Normalize project↔agent relation (join table)
➕ Avoids storing JSON snapshots in a single projects column
➕ Enables querying/reporting across projects/agents via SQL
➕ Simplifies validating referential integrity at DB level
➖ More schema/DAO complexity (new table + migrations + queries)
➖ Requires deciding how/when to snapshot agent versions for reproducibility
2. Store agent IDs only (resolve at runtime)
➕ No JSON serialization/parsing in DB columns
➕ Always runs latest agent definition without extra update steps
➖ Runs are not reproducible if agent prompts change over time
➖ Deletes/edits can break historical meaning of past projects/jobs
3. Embed agents into Job spec only (no project persistence)
➕ Keeps project records minimal; job is fully self-contained
➕ Avoids project update flows and snapshot maintenance
➖ Harder to show configured agents at project/module level later
➖ Users can’t easily re-run with the same configuration without reselecting
Recommendation: The chosen approach (persisting per-project snapshots of selected agents and passing those snapshots into jobs via a job-owned ConfigMap) is a good balance for reproducibility and operational isolation. If long-term analytics/querying becomes important, consider evolving to a normalized join table while retaining immutable snapshots per job/project for auditability.
Files changed (86) +4949 / -76
Enhancement (66) +3938 / -63
createAndInitProject.tsPass adversarialAgentIds through scaffolder project creation+3/-0
Pass adversarialAgentIds through scaffolder project creation
• Extends CreateAndInitProjectParams and the backend project-create request body to optionally include adversarialAgentIds.
adversarialAgents.tsAdd backend CRUD routes for adversarial agents+176/-0
Add backend CRUD routes for adversarial agents
• Introduces GET/list (with optional phase filter), GET by id, POST, PUT, and DELETE endpoints with input validation and admin-write permission enforcement.
projects.tsAttach adversarial agents on project creation and add adversarial-run trigger+131/-0
Attach adversarial agents on project creation and add adversarial-run trigger
• Adds optional adversarialAgentIds to project creation (validating IDs and storing snapshots), and introduces POST /projects/:projectId/adversarial-run to launch adversarial-analyze/migrate jobs with conflict detection.
openapi.yamlAdd OpenAPI definitions for adversarial agents and adversarial-run+311/-1
Add OpenAPI definitions for adversarial agents and adversarial-run
• Extends the API schema with adversarial-agents CRUD endpoints, project adversarial-run trigger, new artifacts and phase enums, and project/module schema fields for adversarial jobs and agent snapshots.
JobResourceBuilder.tsMount adversarial agent ConfigMap and set imagePullPolicy+74/-2
Mount adversarial agent ConfigMap and set imagePullPolicy
• Adds imagePullPolicy support to job specs and introduces a job-owned ConfigMap builder that writes agents.json and mounts it into the job pod for adversarial phases.
KubeService.tsCreate and mount adversarial agents ConfigMap for adversarial phases+51/-1
Create and mount adversarial agents ConfigMap for adversarial phases
• Detects adversarial phases, generates a per-job ConfigMap name, passes it into the job spec, and creates the ConfigMap owned by the Job for GC cleanup.
adversarialAgentOperations.tsImplement adversarial agent DB operations and project snapshot attachment+231/-0
Implement adversarial agent DB operations and project snapshot attachment
• Adds CRUD operations for adversarial_agents, validates agent IDs when attaching to projects, stores snapshots as JSON in projects.adversarial_agents, and supports listing agents with a phase filter.
index.tsExpose adversarial agent operations via X2ADatabaseService API+106/-21
Expose adversarial agent operations via X2ADatabaseService API
• Wires AdversarialAgentOperations into the service, adds public methods for agent CRUD and project snapshot attach/fetch, and enriches Module reads with last adversarial jobs.
• Skips source repo clone for adversarial phases, reads agents.json from the mounted ConfigMap, runs x2a adversarial-run, and publishes markdown/JSON reports as artifacts.
Phase.tsAdd adversarial phases and agent-phase validation helpers+26/-5
Add adversarial phases and agent-phase validation helpers
• Adds adversarial-analyze/migrate phases, exposes adversarialPhases, and introduces adversarialAgentPhaseValues (analyze|migrate) used for agent configuration validation.
adversarialAgents.test.tsAdd integration tests for adversarial agents endpoints+530/-0
Add integration tests for adversarial agents endpoints
• Adds comprehensive tests across supported DBs covering happy paths, filtering, validation errors, and permission-denied behavior for all CRUD endpoints.
202607081000_create_adversarial_agents_table.tsCreate adversarial_agents table and project snapshot column+75/-0
Create adversarial_agents table and project snapshot column
• Adds a new adversarial_agents table, adds projects.adversarial_agents JSON snapshot column, and (PG only) updates jobs.phase constraint; includes full down migration.
The adversarial agents API accepts arbitrary phase strings, but AdversarialAgentEntity only allows
phases in {analyze,migrate}; AdversarialAgentOperations inserts the DB row before
constructing/validating the entity, so invalid phases can be persisted and later break
GET/list/attach flows.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The route schema permits any phase strings, but the domain entity rejects anything outside
{analyze,migrate}. Because the DB insert happens before entity construction, a bad phase can be
committed and then throw, leaving a persisted invalid record.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Adversarial agent creation/update can persist invalid `phases` values because request validation allows any string and the DB insert happens before `AdversarialAgentEntity` validation. If validation throws after the insert, the request fails but the invalid row remains.
## Issue Context
`AdversarialAgentEntity` enforces that each phase is one of `Phase.adversarialAgentPhaseValues()` (`analyze`, `migrate`). The route schema currently allows `z.array(z.string())`, and `createAdversarialAgent` inserts before constructing the entity.
## Fix Focus Areas
- workspaces/x2a/plugins/x2a-backend/src/router/adversarialAgents.ts[86-91]
- workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/adversarialAgentOperations.ts[35-68]
- workspaces/x2a/plugins/x2a-common/src/domain/AdversarialAgent.ts[52-59]
### Implementation notes
- Tighten Zod validation to `phases: z.array(z.enum(['analyze','migrate'])).min(1)`.
- In `createAdversarialAgent` / `updateAdversarialAgent`, validate by constructing `AdversarialAgentEntity` **before** writing, or wrap insert/update + validation in a DB transaction and rollback on error.
- Convert entity validation failures into `InputError` (400) instead of letting a generic `Error` surface as 500.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
2. Non-PG phase constraint stale 🐞 Bug≡ Correctness
Description
The new migration updates the jobs.phase CHECK constraint only when the DB client is Postgres;
upgrades on SQLite/other DBs keep the old constraint, so inserting adversarial job phases can fail
at runtime.
+ if (knex.client.config.client === 'pg') {+ await knex.schema.raw(+ `ALTER TABLE jobs DROP CONSTRAINT IF EXISTS jobs_phase_check`,+ );+ await knex.schema.raw(+ `ALTER TABLE jobs ADD CONSTRAINT jobs_phase_check CHECK (phase IN ('init', 'analyze', 'migrate', 'publish', 'adversarial-analyze', 'adversarial-migrate'))`,+ );+ }
Relevance
●● Moderate
Likely bug for non-PG DBs, but unclear if non-Postgres upgrades are supported/expected here.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The migration explicitly gates the phase constraint update behind client === 'pg', while the
backend creates jobs with phase: 'adversarial-*' and the jobs schema enforces a finite allowed set
via CHECK/checkIn. Without a non-PG migration path, non-Postgres upgrades can retain the pre-PR
allowed phases and reject inserts.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Existing deployments that are not Postgres won’t get the expanded `jobs.phase` constraint during migration, but the backend now creates jobs with phases like `adversarial-analyze`/`adversarial-migrate`. This can cause job creation to fail with a constraint violation.
## Issue Context
- The old `create_jobs_table` migration was edited to include the new phases, but previously-migrated DBs do not re-run it.
- The new migration only alters the CHECK constraint for Postgres.
## Fix Focus Areas
- workspaces/x2a/plugins/x2a-backend/migrations/202607081000_create_adversarial_agents_table.ts[25-33]
- workspaces/x2a/plugins/x2a-backend/migrations/2025012401_create_jobs_table.ts[24-46]
- workspaces/x2a/plugins/x2a-backend/src/router/projects.ts[524-555]
### Implementation notes
- Ensure the `jobs.phase` constraint is updated (or removed/relaxed) for **all supported DB clients**, not just `pg`.
- If SQLite constraint alteration is difficult, consider a migration that rebuilds the table without the restrictive CHECK or uses a DB-agnostic approach.
- Add an integration test for an upgrade scenario where the DB was migrated before this PR and verify adversarial job creation succeeds.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
3. Project create not atomic 🐞 Bug☼ Reliability
Description
POST /projects creates the project row and then attaches adversarial agents; if any agent ID doesn’t
exist, attachAdversarialAgentsToProject throws after the project is already created, leaving a
partially-created project even though the request fails.
Transactional/atomicity change is more invasive; team may accept but not guaranteed for this feature
PR.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The route creates the project before attaching adversarial agents; the attach method explicitly
throws an InputError on missing IDs, so the failure happens after the project is already persisted.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
Project creation is not atomic when `adversarialAgentIds` are provided: the project is created first, then agent attachment can throw, causing a failed request with a persisted project.
## Issue Context
`attachAdversarialAgentsToProject` validates IDs and throws `InputError` when any are missing.
## Fix Focus Areas
- workspaces/x2a/plugins/x2a-backend/src/router/projects.ts[179-199]
- workspaces/x2a/plugins/x2a-backend/src/services/X2ADatabaseService/adversarialAgentOperations.ts[163-205]
### Implementation notes
Pick one:
1) **Pre-validate IDs** before calling `createProject` (e.g., add a `validateAdversarialAgentIdsExist(agentIds)` method that only queries and throws if missing).
2) **Wrap create + attachments in a single transaction** at the DB service layer, rolling back the project insert if attachments fail.
3) If transactions aren’t feasible here, catch attachment failure and explicitly delete the newly created project as compensation.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
4. Stale jobs block adversarial-run✓ Resolved🐞 Bug☼ Reliability
Description
The adversarial-run endpoint rejects runs based only on DB job status without reconciling active
jobs against Kubernetes; stale “running/pending” DB rows can cause repeated 409 responses even when
no K8s job is actually active.
Reconciling DB job state with Kubernetes adds complexity; may be deferred unless it’s a known issue.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The new endpoint checks JobStatus.isActive() directly on DB rows, whereas the existing module run
handler reconciles active jobs against Kubernetes before enforcing the single-active-job constraint.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
`POST /projects/:projectId/adversarial-run` decides “already running” purely from DB status. If the callback never updated the DB (or the K8s job was deleted), users can be blocked from starting a new adversarial job.
## Issue Context
The existing module run endpoint reconciles active jobs via `reconcileJobStatus(...)` before returning 409.
## Fix Focus Areas
- workspaces/x2a/plugins/x2a-backend/src/router/projects.ts[524-541]
- workspaces/x2a/plugins/x2a-backend/src/router/modules.ts[202-229]
### Implementation notes
- Filter to jobs matching the adversarial phase and active statuses, then call `reconcileJobStatus` (or a similar K8s check) before deciding to return 409.
- If reconciliation determines the job is not active, update the DB status accordingly and proceed to create a new job.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
5. Migration filename has extra digits 📘 Rule violation⚙ Maintainability
Description
A new migration file under plugins/x2a-backend/migrations/ does not follow the required
YYYYMMDDHH_description.ts (10-digit timestamp) naming pattern. This can break ordering assumptions
and tooling that depends on the standardized filename format.
Deterministic compliance fix: rename migration to required 10-digit timestamp pattern.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
PR Compliance ID 1877 requires migrations in plugins/x2a-backend/migrations/ to have exactly a
10-digit timestamp prefix. The newly added migration filename uses a 12-digit timestamp
(202607081000_...).
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
The migration filename `202607081000_create_adversarial_agents_table.ts` does not match the required `^[0-9]{10}_[a-z0-9_]+\.ts$` pattern.
## Issue Context
Compliance requires migration filenames to be timestamped with exactly `YYYYMMDDHH` (10 digits) to keep ordering consistent.
## Fix Focus Areas
- workspaces/x2a/plugins/x2a-backend/migrations/202607081000_create_adversarial_agents_table.ts[1-5]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
New production TSX components were added without co-located *.test.tsx files in the same
directory, violating the required test naming/co-location convention. This makes the change harder
to validate and maintain over time.
Co-located test convention is typically enforced; adding a basic component test is straightforward.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
PR Compliance ID 1692 requires a co-located *.test.ts(x) file for each changed TS/TSX source file.
AdversarialAgentsPage.tsx was added, but there is no corresponding co-located
AdversarialAgentsPage.test.tsx in the same directory in this PR branch.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
TypeScript/TSX source files added in `.../AdversarialAgentsPage/` do not have co-located test files using the required `*.test.tsx` suffix.
## Issue Context
Compliance requires tests to live next to the source file and use the `.test.ts` / `.test.tsx` suffix.
## Fix Focus Areas
- workspaces/x2a/plugins/x2a/src/components/AdversarialAgentsPage/AdversarialAgentsPage.tsx[1-69]
- workspaces/x2a/plugins/x2a/src/components/AdversarialAgentsPage/AdversarialAgentsTable.tsx[1-99999]
- workspaces/x2a/plugins/x2a/src/components/AdversarialAgentsPage/AgentDialog.tsx[1-241]
- workspaces/x2a/plugins/x2a/src/components/AdversarialAgentsPage/DeleteAgentDialog.tsx[1-99999]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
7. Endpoint accepts raw SCM token 📘 Rule violation⛨ Security
Description
A new REST endpoint accepts a targetRepoAuth.token string from the request body, which is an
OAuth/PAT-like SCM secret being passed around directly rather than being resolved from the
scaffolder secrets mechanism. This increases the risk of token exposure via logs, client code, and
transport.
Security/compliance: avoid accepting raw SCM tokens in request bodies; likely required to pass
review.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
PR Compliance ID 1959 disallows SCM OAuth tokens being passed/handled as raw strings outside the
scaffolder secrets mechanism. The new endpoint explicitly defines targetRepoAuth with a token
string in the request body schema, indicating the token is provided directly by the caller.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
The new `/projects/:projectId/adversarial-run` endpoint schema accepts `targetRepoAuth: { token: string }` directly from the request body.
## Issue Context
Compliance requires SCM OAuth tokens to be obtained from scaffolder secrets (or an equivalent dedicated secrets abstraction), not passed as plain strings or sourced from ad-hoc locations.
## Fix Focus Areas
- workspaces/x2a/plugins/x2a-backend/src/router/projects.ts[481-546]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds adversarial agents - a way to configure agents that check the LLM's output during conversion.
Tested on RHDH 1.10.2 as a dynamic plugin . created agents, selected them in the scaffolder, ran a conversion, and verified adversarial job details appear on the module page.