Skip to content

ENG-3160: BE - Access policy agent chat (types + prompt explorer)#7992

Open
thabofletcher wants to merge 9 commits intoENG-2959-FE-Astralis-agent-chat-for-policy-buildingfrom
ENG-3160-BE-access-policy-chat
Open

ENG-3160: BE - Access policy agent chat (types + prompt explorer)#7992
thabofletcher wants to merge 9 commits intoENG-2959-FE-Astralis-agent-chat-for-policy-buildingfrom
ENG-3160-BE-access-policy-chat

Conversation

@thabofletcher
Copy link
Copy Markdown
Contributor

@thabofletcher thabofletcher commented Apr 21, 2026

Ticket ENG-3160

Description Of Changes

Backend support changes for the access policy agent chat feature, targeting Lucano's UI branch. This PR adds the TypeScript types needed by the UI and extends the prompt explorer developer tool to support the new chat prompt.

Code Changes

  • Add AccessPoliciesApplicationConfig TypeScript type with agent_enabled field
  • Wire AccessPoliciesApplicationConfig into PlusApplicationConfig
  • Export type from types/api/index.ts
  • Add ACCESS_POLICY_CHAT prompt type and ACCESS_POLICIES category to prompt explorer types
  • Add agentPrompt / currentPolicyYaml editable inputs to QuestionnaireControls for the chat prompt type
  • Extend buildQuestionnaireVariables to handle access_policy_chat
  • Add Access Policies category to the prompt explorer filter

Steps to Confirm

  1. Enable the feature flag: FIDESPLUS__ACCESS_POLICIES__AGENT_ENABLED=true
  2. Navigate to Access Policies → open or create a policy — chat panel should appear on the right
  3. In the prompt explorer (/poc/prompt-explorer), select "Access Policy Agent Chat" — editable User Prompt and Current Policy YAML fields should appear before clicking Render Prompt

Pre-Merge Checklist

  • Issue requirements met
  • All CI pipelines succeeded
  • CHANGELOG.md updated
    • Updates unreleased work already in Changelog, no new entry necessary
  • UX feedback:
    • No UX review needed
  • Followup issues:
    • No followup issues
  • Database migrations:
    • No migrations
  • Documentation:
    • No documentation updates required

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
fides-plus-nightly Ready Ready Preview, Comment Apr 22, 2026 11:35pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
fides-privacy-center Ignored Ignored Apr 22, 2026 11:35pm

Request Review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 22, 2026

Title Lines Statements Branches Functions
admin-ui Coverage: 8%
6.33% (2799/44169) 5.58% (1402/25090) 4.43% (579/13062)
fides-js Coverage: 78%
78.98% (1962/2484) 65.55% (1214/1852) 72.57% (336/463)
privacy-center Coverage: 88%
85.97% (331/385) 81.36% (179/220) 78.87% (56/71)

thabofletcher and others added 7 commits April 22, 2026 13:12
…Config

Adds the `access_policies.agent_enabled` field to the TypeScript config
type so the frontend can read the feature flag from GET /api/v1/config.

Co-Authored-By: Thabo Fletcher <thabo@ethyca.com>
Add ACCESS_POLICY_CHAT type and ACCESS_POLICIES category to the prompt
explorer so the user prompt and current YAML are editable fields before
clicking Render Prompt — matching the pattern used by questionnaire prompts.
@ant-design/x bundles refractor (and react-syntax-highlighter) which
ship as ESM-only packages. Jest's CJS transform config can't handle
them without a full transform exception chain. Instead, mock @ant-design/x
entirely in unit tests — Bubble/Sender are not exercised there anyway.
Change `*/api/v1/plus/llm/access-policy-chat` to `*/api/v1/plus/access-policy/agent`
to match the endpoint defined in the route.
@thabofletcher
Copy link
Copy Markdown
Contributor Author

/code-review

Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Code Review: PR #7992 — BE - Access policy agent chat (types + prompt explorer)

Overall this is a clean, focused change — the URL fix is correct and the new prompt-explorer wiring works logically. Two themes run through most of the findings:

1. Enum members vs. raw strings (all files)

The PR defines PromptCategory.ACCESS_POLICIES and PromptType.ACCESS_POLICY_CHAT in types.ts, then doesn't use them for any of the comparisons it adds. Every new conditional (=== "access_policy_chat", === "access_policies", === "questionnaire") should use the corresponding enum member. See inline comments on utils.ts:68, QuestionnaireControls.tsx:75, prompt-explorer.tsx:175, and prompt-explorer.tsx:321.

2. Generated enum divergence

types/api/models/PromptCategory.ts and PromptType.ts (auto-generated) do not include the new ACCESS_POLICIES / ACCESS_POLICY_CHAT values. The comment in types.ts notes these are "hardcoded until openapi:generate properly generates them," which is an acceptable short-term workaround — but it's worth tracking. If any code path uses the generated enums for comparison (e.g., via PromptInfo.category from the API response), those checks will silently miss the new values. The fix is to re-run npm run openapi:generate once the backend OpenAPI spec includes the new enum members.

3. QuestionnaireControls scope creep

The component now handles two unrelated prompt categories under one name and interface. The 4 new props are irrelevant to all non-access-policy paths. See inline comment on QuestionnaireControls.tsx:265 for a grouping option that keeps the interface manageable without a full refactor.

Minor

  • Default agentPrompt state vs. placeholder text duplication (see inline on prompt-explorer.tsx:82) — nit for a dev-tool page.

🔬 Codegraph: connected (47303 nodes)


💡 Write /code-review in a comment to re-run this review.

@@ -60,7 +66,16 @@ export const buildQuestionnaireVariables = ({
isFinalQuestion = false,
questionToRephrase = "",
previousPhrasings = "",
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.

clients/admin-ui/src/features/prompt-explorer/utils.ts:68

Raw string comparison against enum values. PromptType.ACCESS_POLICY_CHAT exists in types.ts for exactly this purpose:

// current
if (promptType === "access_policy_chat") {

// better
if (promptType === PromptType.ACCESS_POLICY_CHAT) {

Same issue applies to all string comparisons against prompt type/category values in this file (lines 79, 107, 119, 128). Using the enum member means a rename in types.ts is caught at compile time rather than silently breaking at runtime.

setAgentPrompt,
currentPolicyYaml,
setCurrentPolicyYaml,
}: QuestionnaireControlsProps) => {
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.

clients/admin-ui/src/features/prompt-explorer/QuestionnaireControls.tsx:75

Raw string literals in the guard — prefer the enum members that are already defined in types.ts:

// current
!["questionnaire", "access_policies"].includes(selectedPrompt.category)

// better
![PromptCategory.QUESTIONNAIRE, PromptCategory.ACCESS_POLICIES].includes(
  selectedPrompt.category as PromptCategory,
)

This is also a good opportunity to revisit the component name and JSDoc. QuestionnaireControls now handles access-policy prompts too, so the name is misleading. The long-term fix would be a dedicated AccessPolicyChatControls component, but at minimum the JSDoc on line ~65 should be updated to reflect the expanded scope.

</div>
</Card>
)}

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.

clients/admin-ui/src/features/prompt-explorer/QuestionnaireControls.tsx:265

Same raw string issue as the guard: promptType === "access_policy_chat" should be promptType === PromptType.ACCESS_POLICY_CHAT.

Also, the component interface has grown to ~20 props. The 4 new ones (agentPrompt, setAgentPrompt, currentPolicyYaml, setCurrentPolicyYaml) are entirely irrelevant to all non-access-policy paths. Consider either grouping them into an object prop:

accessPolicyChatState?: {
  agentPrompt: string;
  setAgentPrompt: (v: string) => void;
  currentPolicyYaml: string;
  setCurrentPolicyYaml: (v: string) => void;
};

…or extracting an AccessPolicyChatControls component. Either keeps the prop surface manageable as more prompt types are added.

@@ -168,7 +174,8 @@ const PromptExplorer: NextPage = () => {

try {
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.

clients/admin-ui/src/pages/poc/prompt-explorer.tsx:175

Raw string comparisons — both "questionnaire" and "access_policies" should use the enum:

selectedPrompt.category === PromptCategory.QUESTIONNAIRE ||
selectedPrompt.category === PromptCategory.ACCESS_POLICIES

PromptCategory is already imported in this file, so this is a one-line fix.

value={selectedCategory}
onChange={(value) => setSelectedCategory(value)}
options={[
{ label: "Access Policies", value: "access_policies" },
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.

clients/admin-ui/src/pages/poc/prompt-explorer.tsx:321

The new filter dropdown option uses a raw string value. Since selectedCategory is typed as PromptCategory | undefined, prefer the enum member to keep it consistent:

{ label: "Access Policies", value: PromptCategory.ACCESS_POLICIES },

Same applies to the existing "assessment" and "questionnaire" entries while you're here.

@@ -80,6 +80,12 @@ const PromptExplorer: NextPage = () => {
"How long do you keep this data?",
);

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.

clients/admin-ui/src/pages/poc/prompt-explorer.tsx:82

The default state value hard-codes a test prompt:

const [agentPrompt, setAgentPrompt] = useState<string>(
  "Create a policy that denies marketing use for EEA users",
);

The TextArea below already has placeholder="e.g., Create a policy that denies marketing use", so the seeded default and the placeholder are redundant. Either initialize to "" and rely on the placeholder, or keep the default and remove the placeholder. Nit for a dev-tool page, but the current state is slightly confusing ("why is there already text?").

The backend uses control (singular string) but the frontend had controls
(plural array) throughout. This caused 422s on create/update because
extra="forbid" on the request schema rejected the unknown field.

- Rename AccessPolicy.controls to control throughout
- Map SidebarFormValues.controls[0] to control in save handlers
- Fix list filtering, grouping, and table column to use control
- Fix MSW mock data to match

Note: AccessPolicyYaml.controls (the YAML content field) remains a list
— that is a separate concept from the API metadata field.
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.

1 participant