Skip to content

Feat: Add AI-assisted GitHub issue agent POC - #492

Merged
huang195 merged 9 commits into
rossoctl:mainfrom
s-and-p-team:feat/aiac-github-issue-agent-poc
Jun 19, 2026
Merged

Feat: Add AI-assisted GitHub issue agent POC#492
huang195 merged 9 commits into
rossoctl:mainfrom
s-and-p-team:feat/aiac-github-issue-agent-poc

Conversation

@omerboehm

@omerboehm omerboehm commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a proof-of-concept demo for AI-driven Access Control (AIAC) under kagenti-extensions/authbridge/demos/aiac-github-issue.

The demo showcases how an AI agent can automatically infer and configure Keycloak access policies from natural language descriptions — eliminating the need for manually written permission rules.

Key components:

  • PolicyBuilder agent (aiac_agent/) — a LangGraph-based state machine that parses a plain-English policy description and generates structured YAML role mappings, with LLM-based semantic validation and automatic retry logic
  • Keycloak operations (keycloak_ops/) — exports realm config, applies/deletes composite role mappings via the Keycloak admin API
  • CLI orchestrator (aiac_cli.py) — runs the full pipeline: export → generate → delete old policy → apply new policy
  • Setup script (setup_keycloak.py) — idempotently provisions the demo realm with clients, roles (developer, tech-support, sales), and users (alice, bob, charlie)
  • Kubernetes manifests (k8s/) — deployments and AuthBridge configmaps for cluster-based testing
  • Sample policies (policies/) — two example natural language policy files demonstrating different access levels

The AIAC agent integrates with the existing AuthBridge token exchange flow: once policies are applied, Keycloak composite roles ensure that exchanged tokens automatically carry only the scopes allowed by the user's realm role — enforcing access control at the token-issuing layer.

Related issue(s)

Fixes #931

Testing Instructions

  1. Start a local Keycloak instance
  2. Configure aiac.env with Keycloak connection details
  3. Run python setup_keycloak.py config.yaml to provision the demo realm
  4. Run python aiac_cli.py policies/regular_policy.txt to generate and apply a policy
  5. Verify token exchange behavior differs for alice (developer), bob (tech-support), and charlie (sales)

See demo.md for a full walkthrough.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added AI-powered access control policy generation system that converts natural-language policy descriptions into structured YAML for Keycloak.
    • Introduced CLI tool to generate policies, validate them, and apply updates to Keycloak realm configurations.
  • Documentation

    • Added comprehensive demo guide for the AI Access Control workflow with step-by-step setup and testing instructions.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@omerboehm, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 34 minutes and 25 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6b21bc41-0e04-4bf3-a2d8-dd8e1ebd1aa5

📥 Commits

Reviewing files that changed from the base of the PR and between c0b151c and bdce3b3.

📒 Files selected for processing (4)
  • authbridge/demos/github-issue/aiac/Makefile
  • authbridge/demos/github-issue/aiac/scripts/show-result.py
  • authbridge/demos/github-issue/demo-aiac.md
  • authbridge/demos/github-issue/setup_keycloak.py
📝 Walkthrough

Walkthrough

A new AI Access Control (AIAC) demo is added under authbridge/demos/github-issue/aiac/. It introduces a LangGraph-based PolicyBuilder agent that converts natural-language policy descriptions into validated YAML via LLM invocation with retry logic, a keycloak_ops package for exporting realm config and applying/deleting composite-role mappings, an end-to-end aiac_cli.py pipeline, expanded dual-mode setup_keycloak.py RBAC provisioning, and a comprehensive demo-aiac.md walkthrough.

Changes

AIAC Policy Generation and Keycloak Enforcement Flow

Layer / File(s) Summary
Package initialization, workflow state, and configuration system
aiac_agent/__init__.py, aiac_agent/agent/__init__.py, aiac_agent/agent/state.py, aiac_agent/config/__init__.py, aiac_agent/config/config_utils.py, aiac_agent/config/constants.py, aiac_agent/config/llm_config.py, authbridge/requirements.txt
Defines package exports, PolicyState TypedDict for LangGraph state, load_config/extract_realm_roles_and_clients helpers, MAX_VALIDATION_RETRIES constant, and LLMConfig with YAML/env-based ChatOpenAI construction (RITS vs. OpenAI-compatible) and lazy singleton caching.
Prompt engineering, LLM output parsing, and policy validation
aiac_agent/prompts/prompt_builder.py, aiac_agent/prompts/__init__.py, aiac_agent/utils/parsers.py, aiac_agent/utils/validators.py, aiac_agent/utils/__init__.py
Implements build_system_prompt/build_retry_prompt with embedded role/call-chain context, multi-strategy extract_explanation_and_json for LLM output, deterministic validate_policy_structure, and LLM-invoked verify_policy_semantics with transient-error sentinel returns.
LangGraph workflow nodes and PolicyBuilder orchestration
aiac_agent/agent/graph.py
Introduces PolicyBuilderConfig dataclass and stateless node functions (_parse_and_extract_scopes, _build_policy, _generate_yaml, _validate_policy). create_policy_builder_graph wires nodes with conditional retry edges. PolicyBuilder class loads config, compiles the graph, and exposes generate_policy/save_policy.
Keycloak configuration export, apply, and delete operations
keycloak_ops/__init__.py, keycloak_ops/export_config.py, keycloak_ops/apply_policy.py, keycloak_ops/delete_policy.py
export_config collects clients/realm_roles/audience_targets/users from a live Keycloak realm into YAML. apply_access_control_policy validates and writes client-role composites into realm roles. delete_access_control_policy removes all composite mappings per realm role.
Keycloak dual-mode realm provisioning
setup_keycloak.py
Extends the script with manual-mode fixes (ensure_admin_in_realm, main_manual refactor) and a full RBAC pipeline: idempotent client/role/scope creation with audience mappers, fullScopeAllowed disabling, default/optional scope assignment, scope-to-role gating via direct REST, user provisioning, summary printing, and a main_rbac orchestrator with --reset-only support.
aiac_cli.py end-to-end CLI pipeline
aiac/aiac_cli.py
Implements generate_policy_only (reads policy text, builds LLM and PolicyBuilder, saves YAML), _confirm_apply (interactive confirmation), and run_full_pipeline (env validation, config export, policy generation, Keycloak delete/apply sequencing). main() routes between generate-only and full pipeline modes with --yes bypass.
Demo configuration, Makefile workflow, and walkthrough
aiac/config.yaml, aiac/policies/*, aiac/aiac.env.TEMPLATE, aiac_agent/config/llm_conf.yaml.TEMPLATE, aiac/.gitignore, aiac/Makefile, aiac/scripts/show-result.py, demo-aiac.md
Adds demo config.yaml with clients/roles/users/audience targets, regular and permissive policy texts, environment and LLM config templates, Makefile targets (preflight/setup/apply-policy/apply-permissive/show-result/reset), a Keycloak composite-role inspection script, and the complete end-to-end demo walkthrough.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant aiac_cli
  participant export_config
  participant PolicyBuilder
  participant LLM
  participant KeycloakAdmin

  User->>aiac_cli: aiac_cli.py <policy_text> [--yes]
  aiac_cli->>export_config: export_config(realm, output_yaml)
  export_config->>KeycloakAdmin: fetch clients/roles/users/scopes
  KeycloakAdmin-->>export_config: realm RBAC data
  export_config-->>aiac_cli: generated config YAML
  aiac_cli->>PolicyBuilder: generate_policy(description)
  PolicyBuilder->>LLM: _parse_and_extract_scopes (system + user prompt)
  LLM-->>PolicyBuilder: role→scope JSON
  PolicyBuilder->>PolicyBuilder: _build_policy / _generate_yaml
  PolicyBuilder->>LLM: verify_policy_semantics
  LLM-->>PolicyBuilder: ROLES_CORRECT / MAPPINGS_CORRECT
  PolicyBuilder-->>aiac_cli: yaml_output, success, errors
  aiac_cli->>KeycloakAdmin: delete_access_control_policy
  aiac_cli->>KeycloakAdmin: apply_access_control_policy
  KeycloakAdmin-->>aiac_cli: composite roles updated
  aiac_cli-->>User: policy applied
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Suggested reviewers

  • mrsabath

Poem

🐇 A rabbit hops through policy land,
Where LangGraph loops at LLM's command.
Keycloak roles bloom from words in plain text,
YAML appears — what policy comes next?
With retries and graphs the access is set,
The cleverest bunny demo yet! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feat: Add AI-assisted GitHub issue agent POC' clearly summarizes the main addition: a proof-of-concept AI agent for the GitHub issue use case that automates access control policy generation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Fixed
Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Fixed
Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Fixed
Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Fixed
Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Fixed
Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Fixed
Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Fixed
Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Fixed
Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Fixed
@omerboehm omerboehm changed the title Feat: Add AI-assisted GitHub issue agent POC feat: Add AI-assisted GitHub issue agent POC Jun 9, 2026

@coderabbitai coderabbitai 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.

Actionable comments posted: 18

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (16)
authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py-27-37 (1)

27-37: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace Optional[X] with Python 3.12+ union syntax X | None.

The dataclass fields use Optional[str] which is legacy syntax. Per coding guidelines, use str | None instead.

♻️ Proposed fix
 `@dataclass`
 class LLMConfig:
     """Configuration for LLM."""
 
     model: str
-    endpoint: Optional[str]
-    api_key: Optional[str]
+    endpoint: str | None
+    api_key: str | None
     temperature: float
     max_tokens: int
     timeout: int
     retries: int

As per coding guidelines: "Python code must use Python 3.12+ syntax with type hints using modern union syntax (e.g., str | None instead of Optional[str])".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py` around
lines 27 - 37, Update the LLMConfig dataclass to use Python 3.12+ union syntax
for nullable fields: replace any occurrences of Optional[str] on the endpoint
and api_key fields with str | None (leave model as str), and remove or adjust
any unnecessary Optional imports; ensure the class name LLMConfig and field
names endpoint and api_key are updated accordingly so type hints use the modern
union form.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py-15-15 (1)

15-15: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace legacy typing generic with Python 3.12+ built-in type.

The return type uses Dict[str, Any] from the typing module. Per coding guidelines, use dict[str, Any] instead.

♻️ Proposed fix
-def load_config(config_path: Path) -> Dict[str, Any]:
+def load_config(config_path: Path) -> dict[str, Any]:

As per coding guidelines: "Python code must use Python 3.12+ syntax with type hints using modern union syntax" — this includes using dict instead of Dict.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py` at line
15, The function signature for load_config currently uses the legacy typing
generic Dict[str, Any]; update its return annotation to the modern built-in form
dict[str, Any] (and similarly replace any other occurrences of typing.Dict in
the same file if present) so the signature becomes load_config(config_path:
Path) -> dict[str, Any]; keep imports unchanged unless typing.Dict is no longer
used anywhere in the module, in which case remove that import.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py-171-176 (1)

171-176: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace Optional with Python 3.12+ union syntax.

The function signature uses Optional[str] and Optional[Path] which are legacy syntax. Per coding guidelines, use str | None and Path | None.

♻️ Proposed fix
 def create_llm(
-    model_name: Optional[str] = None,
-    env_path: Optional[Path] = None,
-    yaml_path: Optional[Path] = None,
+    model_name: str | None = None,
+    env_path: Path | None = None,
+    yaml_path: Path | None = None,
     verbose: bool = True,
 ) -> BaseChatModel:

As per coding guidelines: "Python code must use Python 3.12+ syntax with type hints using modern union syntax (e.g., str | None instead of Optional[str])".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py` around
lines 171 - 176, The function signature for create_llm uses legacy typing
Optional; update the annotations to Python 3.12+ union syntax by replacing
Optional[str] with str | None and Optional[Path] with Path | None (and any other
Optional usages in the same function signature or nearby vars), keeping the
default values and return type BaseChatModel unchanged; ensure imports remain
valid (remove unused Optional if present).

Source: Coding guidelines

authbridge/demos/aiac-github-issue/aiac_agent/agent/state.py-37-43 (1)

37-43: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace legacy typing generics with Python 3.12+ built-in types.

The field type hints use List[...] and Dict[...] from the typing module. Per coding guidelines, Python 3.12+ code should use the lowercase built-in generic syntax: list[...] and dict[...].

♻️ Proposed fix
-    parsed_scopes: List[Dict[str, Any]]
-    policy_structure: Dict[str, Any]
+    parsed_scopes: list[dict[str, Any]]
+    policy_structure: dict[str, Any]
     yaml_output: str
-    messages: Annotated[List, add]  # Annotated with 'add' for accumulation
-    errors: List[str]  # NOT accumulated - replaced on each validation attempt
+    messages: Annotated[list, add]  # Annotated with 'add' for accumulation
+    errors: list[str]  # NOT accumulated - replaced on each validation attempt
     retry_count: int
     validation_passed: bool  # Boolean flag for retry decision, not accumulated

As per coding guidelines: "Python code must use Python 3.12+ syntax with type hints using modern union syntax (e.g., str | None instead of Optional[str])" — this includes using list and dict instead of List and Dict.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/agent/state.py` around lines 37
- 43, Replace typing module generics with Python 3.12+ builtins for the
annotated fields in the state dataclass: change List[Dict[str, Any]] for
parsed_scopes to list[dict[str, Any]], change Dict[str, Any] for
policy_structure to dict[str, Any], change List for messages and errors to list,
and any other List/Dict occurrences in this file to the lowercase builtins; keep
Annotated[..., add], Any, and other types unchanged and ensure imports remove
unused typing names if applicable.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py-70-70 (1)

70-70: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace Optional with Python 3.12+ union syntax.

The function signature uses Optional[Path] which is legacy syntax. Per coding guidelines, use Path | None.

♻️ Proposed fix
-def load_llm_config_from_yaml(model_name: str, yaml_path: Optional[Path] = None) -> LLMConfig:
+def load_llm_config_from_yaml(model_name: str, yaml_path: Path | None = None) -> LLMConfig:

As per coding guidelines: "Python code must use Python 3.12+ syntax with type hints using modern union syntax (e.g., str | None instead of Optional[str])".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py` at line
70, The function signature for load_llm_config_from_yaml uses legacy
Optional[Path]; update the type hint to Python 3.12+ union syntax by replacing
Optional[Path] with Path | None in the parameter list (i.e., change the
yaml_path annotation in load_llm_config_from_yaml accordingly) and run a quick
type-check to ensure no other references expect typing.Optional import.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py-40-40 (1)

40-40: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace Optional and Dict with Python 3.12+ syntax.

The function signature uses Optional[Path] and Dict[str, Any] which are legacy typing forms. Per coding guidelines, use Path | None and dict[str, Any].

♻️ Proposed fix
-def load_llm_models_yaml(yaml_path: Optional[Path] = None) -> Dict[str, Any]:
+def load_llm_models_yaml(yaml_path: Path | None = None) -> dict[str, Any]:

As per coding guidelines: "Python code must use Python 3.12+ syntax with type hints using modern union syntax (e.g., str | None instead of Optional[str])".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py` at line
40, Update the type hints on the load_llm_models_yaml function signature to use
Python 3.12+ native syntax: replace Optional[Path] with Path | None and replace
Dict[str, Any] with dict[str, Any]; modify the function definition
(load_llm_models_yaml) and any corresponding imports or annotations that
reference Optional or Dict so they’re no longer required.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py-34-36 (1)

34-36: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace legacy typing generics with Python 3.12+ built-in types.

The return type annotation mixes lowercase tuple (correct) with uppercase List and Dict (legacy). Per coding guidelines, use list and dict consistently.

♻️ Proposed fix
 def extract_realm_roles_and_clients(
     config: Dict[str, Any],
-) -> tuple[List[Dict[str, str]], Dict[str, List[Dict[str, str]]], Dict[str, List[str]]]:
+) -> tuple[list[dict[str, str]], dict[str, list[dict[str, str]]], dict[str, list[str]]]:

Also update line 35:

-    config: Dict[str, Any],
+    config: dict[str, Any],

As per coding guidelines: "Python code must use Python 3.12+ syntax with type hints using modern union syntax" — this includes using list and dict instead of List and Dict.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py` around
lines 34 - 36, The return type annotation on the function
extract_realm_roles_and_clients uses legacy typing generics (List, Dict); update
the signature to use built-in generics (list, dict) and modern typing where
needed so the return type becomes tuple[list[dict[str, str]], dict[str,
list[dict[str, str]]], dict[str, list[str]]]; keep the same ordering and
semantics of the three returned structures and only change the type names (and
import usage if present) to the Python 3.12+ built-ins.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py-104-104 (1)

104-104: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace Optional with Python 3.12+ union syntax.

The function signature uses Optional[Path] which is legacy syntax. Per coding guidelines, use Path | None.

♻️ Proposed fix
-def load_llm_config_from_env(env_path: Optional[Path] = None) -> LLMConfig:
+def load_llm_config_from_env(env_path: Path | None = None) -> LLMConfig:

As per coding guidelines: "Python code must use Python 3.12+ syntax with type hints using modern union syntax (e.g., str | None instead of Optional[str])".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py` at line
104, Update the function signature for load_llm_config_from_env to use Python
3.12+ union syntax by replacing Optional[Path] with Path | None for the env_path
parameter, and remove the now-unused Optional import from typing (or update any
other annotations that rely on Optional) so imports remain accurate; keep the
function name and parameter name unchanged.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/aiac_agent/agent/state.py-9-10 (1)

9-10: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use Python 3.12+ built-in generic types instead of typing module classes.

The imports include Dict and List from typing, but per coding guidelines, Python 3.12+ code should use lowercase built-in types (dict, list) directly. The typing.Dict and typing.List forms are legacy syntax.

♻️ Proposed fix
-from typing import Annotated, Any, Dict, List, TypedDict
+from typing import Annotated, Any, TypedDict

As per coding guidelines: "Python code must use Python 3.12+ syntax with type hints using modern union syntax" — this includes using lowercase built-in generic types.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/agent/state.py` around lines 9
- 10, Replace legacy typing generics with built-in generics: remove Dict and
List from the typing import and use the lowercase built-ins in annotations;
update the import line in state.py (currently importing Dict and List) to omit
them and change any uses of Dict[...] and List[...] in functions/classes in this
module to dict[...] and list[...], keeping other typing names (Annotated, Any,
TypedDict) as-is.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py-12-12 (1)

12-12: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use Python 3.12+ built-in types instead of typing module classes.

The imports include Dict and Optional from typing. Per coding guidelines, Python 3.12+ code should use dict directly and X | None instead of Optional[X].

♻️ Proposed fix
-from typing import Any, Dict, Optional
+from typing import Any

As per coding guidelines: "Python code must use Python 3.12+ syntax with type hints using modern union syntax (e.g., str | None instead of Optional[str])" — this includes using dict instead of Dict.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py` at line
12, Replace typing imports with built-in types and modern union syntax: remove
Dict and Optional from the import line in llm_config.py and use lowercase dict
in type annotations and X | None for optional types throughout the module
(search for any uses of Dict[...] and Optional[...] and update them
accordingly); keep Any if still needed or replace with builtins when applicable.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py-9-10 (1)

9-10: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use Python 3.12+ built-in generic types instead of typing module classes.

The imports include Dict and List from typing. Per coding guidelines, Python 3.12+ code should use lowercase built-in types (dict, list) directly instead of the legacy typing module classes.

♻️ Proposed fix
-from typing import Any, Dict, List
+from typing import Any

As per coding guidelines: "Python code must use Python 3.12+ syntax with type hints using modern union syntax" — this includes using lowercase built-in generic types.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py` around
lines 9 - 10, Replace usages of typing.Dict and typing.List in config_utils.py
with the built-in generic types dict and list and remove Dict and List from the
import list; update the import line to only import Any if still needed (or
remove typing entirely if not), and update any type annotations in
functions/classes that reference Dict or List (e.g., signatures or variable
annotations) to use dict[...] and list[...] with the same type parameters.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/demo.md-3-3 (1)

3-3: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix user-facing grammar/spelling typos in setup instructions.

There are visible wording issues here (e.g., “AI based” and “githb”) that should be corrected for clarity in the walkthrough.

Also applies to: 396-396

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/demo.md` at line 3, Fix the typos and
wording in the demo text: change "AI based" to "AI-based" and correct "githb" to
"GitHub" (and any other similar misspellings). Update the occurrences in
authbridge/demos/aiac-github-issue/demo.md, including the instance around the
shown line with the phrase "AI based" and the other occurrence around line 396,
to ensure consistent, user-facing copy (use "AI-based" and "GitHub" throughout).

Source: Linters/SAST tools

authbridge/demos/aiac-github-issue/demo.md-22-22 (1)

22-22: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize code block formatting for markdown rendering consistency.

Please add explicit language tags to fenced code blocks and convert the indented code block at Line 84 to fenced style so snippets render consistently and are easier to copy/run.

Also applies to: 84-84, 92-92, 211-211, 216-216, 229-229, 254-254, 268-268, 326-326, 341-341, 512-512, 530-530

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/demo.md` at line 22, Convert all code
snippets to fenced triple-backtick blocks and add explicit language tags (e.g.,
```bash, ```js, ```python) for each fenced block; locate any indented code block
(4-space or tab-prefixed) and replace it with a fenced block using the correct
language tag, and then audit the file for other fenced blocks missing language
tags and add the appropriate tag so all examples render consistently and are
copy/paste-ready.

Source: Linters/SAST tools

authbridge/demos/aiac-github-issue/aiac_agent/agent/graph.py-36-37 (1)

36-37: ⚠️ Potential issue | 🟡 Minor

Use Python 3.12 typing syntax in authbridge/demos/aiac-github-issue/aiac_agent/agent/graph.py

This file still uses Optional/Dict typing forms instead of the project’s modern Python 3.12 style.

from typing import Any, Dict, Optional

Update:

  • llm: Optional[BaseChatModel] = Nonellm: BaseChatModel | None = None (around 433-439)
  • generate_policy(...) -> Dict[str, Any]-> dict[str, Any] (around 502-503)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/agent/graph.py` around lines 36
- 37, Replace legacy typing imports and annotations in graph.py with Python 3.12
union and builtin generic syntax: remove Optional and Dict uses and update the
annotation for the llm variable (llm: Optional[BaseChatModel] = None → llm:
BaseChatModel | None = None) and the generate_policy signature
(generate_policy(...) -> Dict[str, Any] → -> dict[str, Any]); search for other
Optional[...] and Dict[...] occurrences (and any from typing import Optional,
Dict) and convert them similarly to | unions and builtin dict/list generics,
keeping the same types (e.g., Optional[X] → X | None, Dict[K, V] → dict[K, V])
so all annotations in the file use Python 3.12 style.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/keycloak_ops/delete_policy.py-9-10 (1)

9-10: ⚠️ Potential issue | 🟡 Minor

Modernize typing generics in this module to the Python 3.12 style.

delete_policy.py still uses typing.Dict/List/Set (from typing import Any, Dict, List, Set, plus -> Dict[...] / -> List[...] / Set[str] at lines 15, 22, and 75). Use built-in generics (dict[...], list[...], set[...]) instead to match modern typing conventions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/keycloak_ops/delete_policy.py` around
lines 9 - 10, The module currently imports Dict/List/Set from typing and uses
those in annotations; change the import to only "from typing import Any" and
replace all uses of Dict[...] with dict[...], List[...] with list[...], and
Set[...] (e.g. Set[str]) with set[str] in the function signatures and variable
annotations in this file so annotations use the native generics (update the
return types and parameter types where Dict/List/Set appear).

Source: Coding guidelines

authbridge/demos/aiac-github-issue/policies/regular_policy.txt-1-2 (1)

1-2: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use official product capitalization in policy text.

Line 1 and Line 2 should use GitHub (not github) for user-facing documentation quality.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/policies/regular_policy.txt` around lines
1 - 2, Update the policy text in regular_policy.txt to use the official product
capitalization "GitHub" instead of "github" in both bullet points (the line "*
Members of the R&D can access both private and public github repositories" and
the line "* Other technical personnel can access public github repositories
only"); replace each occurrence of "github" with "GitHub" so user-facing
documentation uses correct branding.

Source: Linters/SAST tools

🧹 Nitpick comments (7)
authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py (1)

61-62: ⚡ Quick win

Specify encoding when opening text files.

The YAML file is opened without an explicit encoding parameter. This can cause cross-platform issues, as the default encoding varies by system.

♻️ Proposed fix
-    with open(yaml_path, "r") as f:
+    with open(yaml_path, "r", encoding="utf-8") as f:
         config = yaml.safe_load(f)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py` around
lines 61 - 62, Open the YAML file using an explicit encoding to avoid
cross-platform defaults: update the file-open call that currently uses
open(yaml_path, "r") in llm_config.py (the block referencing yaml_path and
yaml.safe_load) to pass encoding="utf-8" (i.e., open(yaml_path, "r",
encoding="utf-8")). Ensure the rest of the logic (reading into config and
yaml.safe_load) remains unchanged.
authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py (2)

72-77: ⚡ Quick win

Add defensive check for required 'name' key in role dictionaries.

When role is a dict, the code directly accesses role["name"] without verifying the key exists. If the configuration contains a malformed role dict missing the "name" field, this will raise a KeyError.

🛡️ Proposed fix
         if isinstance(role, dict):
             # New format with name and description
-            realm_roles.append({"name": role["name"], "description": role.get("description", "")})
+            if "name" not in role:
+                raise ValueError(f"Role dict missing required 'name' field: {role}")
+            realm_roles.append({"name": role["name"], "description": role.get("description", "")})
         else:

The same pattern should be applied to the client roles parsing at lines 91-93.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py` around
lines 72 - 77, When iterating roles, don't access role["name"] blindly; add a
defensive check that when role is a dict it contains a non-empty "name" key (and
is a string), otherwise skip the entry or raise a clear ValueError/log a warning
and continue; update the realm_roles append logic to use role.get("name") after
validation and preserve role.get("description", "") for description. Apply the
identical defensive validation to the client_roles parsing (the same pattern
around the client role handling) so malformed dicts won't raise KeyError and
will produce an explicit, actionable error or log entry.

30-31: ⚡ Quick win

Specify encoding when opening text files.

The file is opened without an explicit encoding parameter. This can cause cross-platform issues, as the default encoding varies by system (e.g., cp1252 on Windows, UTF-8 on Linux).

♻️ Proposed fix
-    with open(config_path, "r") as f:
+    with open(config_path, "r", encoding="utf-8") as f:
         return yaml.safe_load(f)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py` around
lines 30 - 31, The file is opening the config file without specifying text
encoding which can cause cross-platform issues; update the open call that reads
config_path (the block returning yaml.safe_load(f)) to pass an explicit encoding
(e.g., encoding="utf-8") when opening for reading, so change the open(...) in
the function that reads config_path to open(config_path, "r", encoding="utf-8")
and then call yaml.safe_load on the file handle as before.
authbridge/demos/aiac-github-issue/aiac_agent/utils/validators.py (1)

10-23: ⚡ Quick win

Align validator type hints with Python 3.12 conventions.

Please replace typing.Dict/List/Tuple usages with built-in generics in signatures and annotations.

As per coding guidelines, authbridge/**/*.py must use Python 3.12+ type hint syntax.

Also applies to: 99-105

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/utils/validators.py` around
lines 10 - 23, Update type hints to use Python 3.12 built-in generics instead of
typing.Dict/List/Tuple: change the signature of validate_policy_structure to use
dict[str, Any], list[dict[str, str]], list[str], and dict[str, list[dict[str,
str]]], and replace other occurrences of typing.Dict/typing.List/typing.Tuple in
this module (including the functions around the region reported) with the
corresponding built-in generics; keep imports only for names you still need
(e.g., Any) and remove unused typing imports.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/aiac_agent/prompts/prompt_builder.py (1)

9-16: ⚡ Quick win

Adopt Python 3.12 typing syntax in these function signatures.

Please replace typing.List/Dict style with built-in generics (list[...], dict[...]) to match repository typing standards.

As per coding guidelines, authbridge/**/*.py must use Python 3.12+ type hint syntax (e.g., modern union syntax and contemporary annotations).

Also applies to: 248-248

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/prompts/prompt_builder.py`
around lines 9 - 16, Update the type hints in build_system_prompt to use Python
3.12 built-in generics: replace typing.List and typing.Dict with list[...] and
dict[...] for the parameters realm_roles, client_roles_map, and
client_audience_targets; ensure any other signatures in this module using
typing.List/Dict (e.g., the other function noted in the file) are updated
similarly to match the repository's Python 3.12 typing style while keeping
parameter names and return type unchanged.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/aiac_agent/utils/parsers.py (1)

11-15: ⚡ Quick win

Update parser annotations to Python 3.12 style.

Use built-in generics (list, tuple) and add an explicit -> None return annotation for print_explanation to keep typing consistent with project standards.

As per coding guidelines, authbridge/**/*.py must use Python 3.12+ type hint syntax.

Also applies to: 100-100

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/utils/parsers.py` around lines
11 - 15, The type annotations in extract_explanation_and_json and
print_explanation should use Python 3.12 built-in generics and include an
explicit return annotation: change Tuple[str, List] to tuple[str, list] in
extract_explanation_and_json, and add -> None to the signature of
print_explanation; update any matching import usage (remove typing.Tuple/List if
now unused) and ensure the function signatures (extract_explanation_and_json and
print_explanation) follow the new syntax consistently across the file.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/keycloak_ops/apply_policy.py (1)

73-73: ⚡ Quick win

Use Python 3.12 union syntax in type hints.

Line 73 should use dict[str, str] | None instead of Optional[Dict[str, str]] for consistency with the repository typing rule.

As per coding guidelines, authbridge/**/*.py must use Python 3.12+ type hints with modern union syntax (str | None style).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/keycloak_ops/apply_policy.py` at line 73,
Update the type hint for the parameter scope_ids in apply_policy.py from
Optional[Dict[str, str]] to the Python 3.12 union form dict[str, str] | None;
also remove or adjust any now-unused typing imports (Optional, Dict) and ensure
any other occurrences in the function signature or annotations use the modern
union syntax (reference: the scope_ids parameter in the relevant function
definition).

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@authbridge/demos/aiac-github-issue/aiac_agent/agent/graph.py`:
- Around line 266-267: The retry logic is off-by-one: don't increment
retry_count before testing the budget; update the conditional to check whether
retry_count + 1 <= max_retries before returning a state with retry_count + 1
(i.e., replace the current "if structural_errors and retry_count < max_retries"
with a check that ensures the incremented count is still within budget), and
make the same change for the analogous check around retry_count usage at the
later block (the code around retry_count and max_retries at the other
occurrence).

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/llm_conf.yaml.TEMPLATE`:
- Around line 10-12: Replace literal template tokens with env-expandable
placeholders: change the endpoint, api_key and temperature values in this LLM
config template from the current "<...>" text to ${ENV_VAR} style placeholders
(e.g. use ${LLM_ENDPOINT}, ${LLM_API_KEY}, ${LLM_TEMPERATURE} or other
project-standard names) so runtime/env substitution works; apply the same
replacement for the repeated blocks referenced (the occurrences for
endpoint/api_key/temperature at the other ranges) and ensure the YAML values
remain unquoted or appropriately quoted per YAML rules to allow env expansion.

In `@authbridge/demos/aiac-github-issue/aiac_agent/utils/validators.py`:
- Around line 283-288: The except block in validators.py currently returns
(True, True, "Verification skipped due to API rate limiting") for API errors
which incorrectly signals a successful semantic verification; change that return
so the validator does NOT mark the policy as valid — e.g., return (False, False,
"Verification skipped due to API rate limiting") — keeping the error message but
ensuring the boolean pass/fail flags reflect a non-successful verification
(update the tuple returned in the except Exception as e handler where "Already
borrowed" or "BadRequestError" is checked).

In `@authbridge/demos/aiac-github-issue/aiac.env`:
- Around line 3-4: The committed file contains hardcoded admin credentials
(KEYCLOAK_ADMIN_USERNAME and KEYCLOAK_ADMIN_PASSWORD); replace these literal
values with non-sensitive placeholders (e.g., empty values or template tokens)
and require callers to supply real secrets at runtime or via a secrets manager,
add a .env.template (or document in README) showing the placeholder keys, and
ensure the actual aiac.env is excluded from VCS (gitignored) so real credentials
are never committed.

In `@authbridge/demos/aiac-github-issue/config.yaml`:
- Line 6: Replace the hardcoded value for the YAML key "secret" (currently
"demo-ui-secret") with an env-expanded placeholder like ${AUTHBRIDGE_UI_SECRET}
in the config.yaml, and update the authbridge config-loading/startup parsing
logic that reads these YAML files to perform environment variable expansion so
the placeholder is resolved at runtime; ensure the loader treats ${VAR} syntax
(for keys such as "secret") and falls back or errors if the env var is missing
per existing validation rules.

In `@authbridge/demos/aiac-github-issue/k8s/git-issue-agent-deployment.yaml`:
- Around line 53-57: The container spec for the container named "agent" is
missing a hardened securityContext; update the Pod/Container spec (the container
with name "agent" in the Deployment manifest) to enforce non-root and disable
privilege escalation by adding a securityContext that sets runAsNonRoot: true,
specify a non-zero runAsUser (e.g. 1000) at pod or container level, set
allowPrivilegeEscalation: false, drop all Linux capabilities and enable
readOnlyRootFilesystem where appropriate; apply the same changes to the second
container block referenced (the other "agent" container at the later block).

In `@authbridge/demos/aiac-github-issue/k8s/github-tool-deployment.yaml`:
- Around line 43-49: Add a pod/container securityContext to enforce least
privilege for the github-tool container: inside the container spec for the
container named "github-tool" (the entry with image
ghcr.io/kagenti/agent-examples/github-tool:latest) add a securityContext that
sets runAsNonRoot: true, runAsUser (e.g., 1000) or runAsGroup as appropriate,
disallows privilege escalation with allowPrivilegeEscalation: false, drops
capabilities (capabilities: drop: ["ALL"]), and enables readOnlyRootFilesystem:
true (plus seccomp/apparmor profile if available); apply the same
securityContext to the other github-tool container block referenced (lines
78-85) so both containers enforce non-root, no privilege escalation, and
readonly root filesystem.

In `@authbridge/demos/aiac-github-issue/keycloak_ops/apply_policy.py`:
- Around line 93-94: The current broad except around adding a client role (the
block that prints "Client role '{client_name}.{role_name}' already in composite
or error: {e}") swallows all errors; change it to only handle the
idempotent/already-exists case (e.g., detect the API/HTTP 409 or the specific
Keycloak SDK exception for an existing composite) and log/continue, but re-raise
or propagate any other exceptions (network, auth, permission, etc.) so failures
don't get hidden; locate the try/except surrounding the composite role add
(references to client_name and role_name) and replace the generic except
Exception with a conditional catch/inspection of the error type/status and
re-raise unexpected errors.
- Around line 22-26: Validate the result of yaml.safe_load before using it:
after loading into policy_config in apply_policy.py, check that policy_config is
a mapping (e.g., isinstance(policy_config, dict)) and raise a clear ValueError
(including access_control_policy_file) if it's None or not a dict, then safely
read policy = policy_config.get("policy", {}) or {}; this ensures the code in
apply_policy.py fails fast with a clear validation error for empty or
non-mapping YAML documents.

In `@authbridge/demos/aiac-github-issue/keycloak_ops/delete_policy.py`:
- Around line 43-64: The try/except in remove_all_composites_from_realm_role is
swallowing errors (printing them and continuing) so composite-removal failures
become non-fatal; change the handler to surface failures instead of silently
continuing by either removing the try/except so exceptions propagate or by
re-raising after logging (e.g., log the error with context for
realm/realm_role_name and then raise), ensuring the call sites see the failure;
apply the same change to the other composite-removal block that uses
get_realm_role_composites and admin.connection.raw_delete so cleanup errors are
not hidden.

In `@authbridge/demos/aiac-github-issue/keycloak_ops/export_config.py`:
- Around line 156-157: The except block that currently swallows all errors (the
bare "except Exception: pass") during role discovery/audience inference must be
replaced so failures are visible and handled safely: catch the exception as e,
log it (using the module logger or processLogger) with context mentioning role
discovery/audience inference and the affected client_audience_targets, and then
either set client_audience_targets to a safe default (e.g., empty list) or
re-raise the exception depending on caller expectations; locate the try/except
around the role fetch/audience inference and update that handler to use "except
Exception as e: logger.exception(...)" and apply the safe default to
client_audience_targets instead of silently passing.
- Around line 199-209: The code loads the wrong env file causing missing creds;
update the load call in export_config.py so load_dotenv uses the demo env
filename ("aiac.env") instead of ".env" (locate the load_dotenv(script_dir /
".env") call), keep using script_dir and ensure KEYCLOAK_URL,
KEYCLOAK_ADMIN_USERNAME, and KEYCLOAK_ADMIN_PASSWORD checks remain unchanged so
export_config() will validate presence of credentials from aiac.env.

In `@authbridge/demos/aiac-github-issue/requirements.txt`:
- Line 2: Replace all non-exact dependency specs in the requirements file with
exact pins using == for the listed packages: change "pre-commit",
"langgraph>=0.2", "langchain-core>=0.2", "langchain-openai>=1.2", "pydantic>=2",
"PyYAML>=6", and "python-dotenv>=1" to concrete versions (e.g.
pre-commit==x.y.z, langgraph==x.y.z, langchain-core==x.y.z,
langchain-openai==x.y.z, pydantic==x.y.z, PyYAML==x.y.z, python-dotenv==x.y.z)
consistent with your project's lockfile or the latest tested compatible release;
ensure the exact == pins are committed in place of the current unpinned or >=
specs.

In `@authbridge/demos/aiac-github-issue/setup_keycloak.py`:
- Around line 309-313: The code currently prints plaintext secrets using the
client_secret variable in the conditional block around
client_config.get("secret"); remove any logging of client_secret (no f-strings
or prints that include the secret) and instead print a non-secret placeholder
such as "    Secret: (set)" or "    Secret: (preserved from existing client)"
depending on client_config.get("secret"); update the logic in the same
function/block in setup_keycloak.py to ensure client_secret is never written to
logs or stdout.
- Around line 446-465: The scope_ids map in create_client_scopes is keyed only
by scope_name so identical role names across clients collide; change the key to
a unique composite (e.g., "{client_name}:{scope_name}" or similar) when
assigning scope_ids and return that map, and then update any lookup sites that
currently expect plain scope_name (calls that use create_client_scopes results
and later bind scopes/audience) to construct the same composite key when
retrieving the scope id; locate create_client_scopes and
create_single_client_scope plus downstream bind/assignment code that consumes
scope_ids and ensure both creation and lookup use the same composite-key
convention so entries no longer overwrite each other.
- Around line 559-560: Replace the silent "except Exception: pass" in the
scope-assignment try/except blocks with proper error handling: catch Exception
as e and call the module logger (or processLogger) with logger.exception or
logger.error including contextual text (e.g., "failed assigning scope XYZ") so
the stack/exception is recorded, and re-raise or return a failure result for
non-idempotent provisioning errors; for idempotent/expected conflicts you may
suppress after logging. Update both occurrences that currently use "except
Exception: pass".
- Around line 49-55: load_main_config currently opens and parses YAML raw;
change it to read the file as text, expand ${ENV_VAR} (use os.path.expandvars or
equivalent) before calling yaml.safe_load, then validate a required "mode" field
in the resulting dict against the process startup mode (e.g.
os.getenv("STARTUP_MODE") or provided expected mode) and raise a descriptive
ValueError if missing or mismatched; keep the existing FileNotFoundError
behavior and ensure these checks live inside load_main_config so configs under
authbridge/**/*.{yaml,yml} support env expansion and startup mode validation.
- Around line 248-258: delete_realm_roles currently assumes realm_roles_config
contains strings, but the demo uses dicts like {"name", "description"} so
deletions fail; update delete_realm_roles to accept either strings or dicts and
extract the role name before calling KeycloakAdmin.delete_realm_role (e.g.,
check if each item is a dict and use item.get("name") or fallback to the item
itself), handle missing names by skipping/logging, and keep the existing
print/error messages referencing the resolved role_name.

---

Minor comments:
In `@authbridge/demos/aiac-github-issue/aiac_agent/agent/graph.py`:
- Around line 36-37: Replace legacy typing imports and annotations in graph.py
with Python 3.12 union and builtin generic syntax: remove Optional and Dict uses
and update the annotation for the llm variable (llm: Optional[BaseChatModel] =
None → llm: BaseChatModel | None = None) and the generate_policy signature
(generate_policy(...) -> Dict[str, Any] → -> dict[str, Any]); search for other
Optional[...] and Dict[...] occurrences (and any from typing import Optional,
Dict) and convert them similarly to | unions and builtin dict/list generics,
keeping the same types (e.g., Optional[X] → X | None, Dict[K, V] → dict[K, V])
so all annotations in the file use Python 3.12 style.

In `@authbridge/demos/aiac-github-issue/aiac_agent/agent/state.py`:
- Around line 37-43: Replace typing module generics with Python 3.12+ builtins
for the annotated fields in the state dataclass: change List[Dict[str, Any]] for
parsed_scopes to list[dict[str, Any]], change Dict[str, Any] for
policy_structure to dict[str, Any], change List for messages and errors to list,
and any other List/Dict occurrences in this file to the lowercase builtins; keep
Annotated[..., add], Any, and other types unchanged and ensure imports remove
unused typing names if applicable.
- Around line 9-10: Replace legacy typing generics with built-in generics:
remove Dict and List from the typing import and use the lowercase built-ins in
annotations; update the import line in state.py (currently importing Dict and
List) to omit them and change any uses of Dict[...] and List[...] in
functions/classes in this module to dict[...] and list[...], keeping other
typing names (Annotated, Any, TypedDict) as-is.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py`:
- Line 15: The function signature for load_config currently uses the legacy
typing generic Dict[str, Any]; update its return annotation to the modern
built-in form dict[str, Any] (and similarly replace any other occurrences of
typing.Dict in the same file if present) so the signature becomes
load_config(config_path: Path) -> dict[str, Any]; keep imports unchanged unless
typing.Dict is no longer used anywhere in the module, in which case remove that
import.
- Around line 34-36: The return type annotation on the function
extract_realm_roles_and_clients uses legacy typing generics (List, Dict); update
the signature to use built-in generics (list, dict) and modern typing where
needed so the return type becomes tuple[list[dict[str, str]], dict[str,
list[dict[str, str]]], dict[str, list[str]]]; keep the same ordering and
semantics of the three returned structures and only change the type names (and
import usage if present) to the Python 3.12+ built-ins.
- Around line 9-10: Replace usages of typing.Dict and typing.List in
config_utils.py with the built-in generic types dict and list and remove Dict
and List from the import list; update the import line to only import Any if
still needed (or remove typing entirely if not), and update any type annotations
in functions/classes that reference Dict or List (e.g., signatures or variable
annotations) to use dict[...] and list[...] with the same type parameters.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py`:
- Around line 27-37: Update the LLMConfig dataclass to use Python 3.12+ union
syntax for nullable fields: replace any occurrences of Optional[str] on the
endpoint and api_key fields with str | None (leave model as str), and remove or
adjust any unnecessary Optional imports; ensure the class name LLMConfig and
field names endpoint and api_key are updated accordingly so type hints use the
modern union form.
- Around line 171-176: The function signature for create_llm uses legacy typing
Optional; update the annotations to Python 3.12+ union syntax by replacing
Optional[str] with str | None and Optional[Path] with Path | None (and any other
Optional usages in the same function signature or nearby vars), keeping the
default values and return type BaseChatModel unchanged; ensure imports remain
valid (remove unused Optional if present).
- Line 70: The function signature for load_llm_config_from_yaml uses legacy
Optional[Path]; update the type hint to Python 3.12+ union syntax by replacing
Optional[Path] with Path | None in the parameter list (i.e., change the
yaml_path annotation in load_llm_config_from_yaml accordingly) and run a quick
type-check to ensure no other references expect typing.Optional import.
- Line 40: Update the type hints on the load_llm_models_yaml function signature
to use Python 3.12+ native syntax: replace Optional[Path] with Path | None and
replace Dict[str, Any] with dict[str, Any]; modify the function definition
(load_llm_models_yaml) and any corresponding imports or annotations that
reference Optional or Dict so they’re no longer required.
- Line 104: Update the function signature for load_llm_config_from_env to use
Python 3.12+ union syntax by replacing Optional[Path] with Path | None for the
env_path parameter, and remove the now-unused Optional import from typing (or
update any other annotations that rely on Optional) so imports remain accurate;
keep the function name and parameter name unchanged.
- Line 12: Replace typing imports with built-in types and modern union syntax:
remove Dict and Optional from the import line in llm_config.py and use lowercase
dict in type annotations and X | None for optional types throughout the module
(search for any uses of Dict[...] and Optional[...] and update them
accordingly); keep Any if still needed or replace with builtins when applicable.

In `@authbridge/demos/aiac-github-issue/demo.md`:
- Line 3: Fix the typos and wording in the demo text: change "AI based" to
"AI-based" and correct "githb" to "GitHub" (and any other similar misspellings).
Update the occurrences in authbridge/demos/aiac-github-issue/demo.md, including
the instance around the shown line with the phrase "AI based" and the other
occurrence around line 396, to ensure consistent, user-facing copy (use
"AI-based" and "GitHub" throughout).
- Line 22: Convert all code snippets to fenced triple-backtick blocks and add
explicit language tags (e.g., ```bash, ```js, ```python) for each fenced block;
locate any indented code block (4-space or tab-prefixed) and replace it with a
fenced block using the correct language tag, and then audit the file for other
fenced blocks missing language tags and add the appropriate tag so all examples
render consistently and are copy/paste-ready.

In `@authbridge/demos/aiac-github-issue/keycloak_ops/delete_policy.py`:
- Around line 9-10: The module currently imports Dict/List/Set from typing and
uses those in annotations; change the import to only "from typing import Any"
and replace all uses of Dict[...] with dict[...], List[...] with list[...], and
Set[...] (e.g. Set[str]) with set[str] in the function signatures and variable
annotations in this file so annotations use the native generics (update the
return types and parameter types where Dict/List/Set appear).

In `@authbridge/demos/aiac-github-issue/policies/regular_policy.txt`:
- Around line 1-2: Update the policy text in regular_policy.txt to use the
official product capitalization "GitHub" instead of "github" in both bullet
points (the line "* Members of the R&D can access both private and public github
repositories" and the line "* Other technical personnel can access public github
repositories only"); replace each occurrence of "github" with "GitHub" so
user-facing documentation uses correct branding.

---

Nitpick comments:
In `@authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py`:
- Around line 72-77: When iterating roles, don't access role["name"] blindly;
add a defensive check that when role is a dict it contains a non-empty "name"
key (and is a string), otherwise skip the entry or raise a clear ValueError/log
a warning and continue; update the realm_roles append logic to use
role.get("name") after validation and preserve role.get("description", "") for
description. Apply the identical defensive validation to the client_roles
parsing (the same pattern around the client role handling) so malformed dicts
won't raise KeyError and will produce an explicit, actionable error or log
entry.
- Around line 30-31: The file is opening the config file without specifying text
encoding which can cause cross-platform issues; update the open call that reads
config_path (the block returning yaml.safe_load(f)) to pass an explicit encoding
(e.g., encoding="utf-8") when opening for reading, so change the open(...) in
the function that reads config_path to open(config_path, "r", encoding="utf-8")
and then call yaml.safe_load on the file handle as before.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py`:
- Around line 61-62: Open the YAML file using an explicit encoding to avoid
cross-platform defaults: update the file-open call that currently uses
open(yaml_path, "r") in llm_config.py (the block referencing yaml_path and
yaml.safe_load) to pass encoding="utf-8" (i.e., open(yaml_path, "r",
encoding="utf-8")). Ensure the rest of the logic (reading into config and
yaml.safe_load) remains unchanged.

In `@authbridge/demos/aiac-github-issue/aiac_agent/prompts/prompt_builder.py`:
- Around line 9-16: Update the type hints in build_system_prompt to use Python
3.12 built-in generics: replace typing.List and typing.Dict with list[...] and
dict[...] for the parameters realm_roles, client_roles_map, and
client_audience_targets; ensure any other signatures in this module using
typing.List/Dict (e.g., the other function noted in the file) are updated
similarly to match the repository's Python 3.12 typing style while keeping
parameter names and return type unchanged.

In `@authbridge/demos/aiac-github-issue/aiac_agent/utils/parsers.py`:
- Around line 11-15: The type annotations in extract_explanation_and_json and
print_explanation should use Python 3.12 built-in generics and include an
explicit return annotation: change Tuple[str, List] to tuple[str, list] in
extract_explanation_and_json, and add -> None to the signature of
print_explanation; update any matching import usage (remove typing.Tuple/List if
now unused) and ensure the function signatures (extract_explanation_and_json and
print_explanation) follow the new syntax consistently across the file.

In `@authbridge/demos/aiac-github-issue/aiac_agent/utils/validators.py`:
- Around line 10-23: Update type hints to use Python 3.12 built-in generics
instead of typing.Dict/List/Tuple: change the signature of
validate_policy_structure to use dict[str, Any], list[dict[str, str]],
list[str], and dict[str, list[dict[str, str]]], and replace other occurrences of
typing.Dict/typing.List/typing.Tuple in this module (including the functions
around the region reported) with the corresponding built-in generics; keep
imports only for names you still need (e.g., Any) and remove unused typing
imports.

In `@authbridge/demos/aiac-github-issue/keycloak_ops/apply_policy.py`:
- Line 73: Update the type hint for the parameter scope_ids in apply_policy.py
from Optional[Dict[str, str]] to the Python 3.12 union form dict[str, str] |
None; also remove or adjust any now-unused typing imports (Optional, Dict) and
ensure any other occurrences in the function signature or annotations use the
modern union syntax (reference: the scope_ids parameter in the relevant function
definition).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 225c549e-d705-4820-baa4-8f6096aa5754

📥 Commits

Reviewing files that changed from the base of the PR and between 0680516 and fe62c8f.

📒 Files selected for processing (29)
  • authbridge/demos/aiac-github-issue/aiac.env
  • authbridge/demos/aiac-github-issue/aiac_agent/__init__.py
  • authbridge/demos/aiac-github-issue/aiac_agent/agent/__init__.py
  • authbridge/demos/aiac-github-issue/aiac_agent/agent/graph.py
  • authbridge/demos/aiac-github-issue/aiac_agent/agent/state.py
  • authbridge/demos/aiac-github-issue/aiac_agent/config/__init__.py
  • authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py
  • authbridge/demos/aiac-github-issue/aiac_agent/config/constants.py
  • authbridge/demos/aiac-github-issue/aiac_agent/config/llm_conf.yaml.TEMPLATE
  • authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py
  • authbridge/demos/aiac-github-issue/aiac_agent/prompts/__init__.py
  • authbridge/demos/aiac-github-issue/aiac_agent/prompts/prompt_builder.py
  • authbridge/demos/aiac-github-issue/aiac_agent/utils/__init__.py
  • authbridge/demos/aiac-github-issue/aiac_agent/utils/parsers.py
  • authbridge/demos/aiac-github-issue/aiac_agent/utils/validators.py
  • authbridge/demos/aiac-github-issue/aiac_cli.py
  • authbridge/demos/aiac-github-issue/config.yaml
  • authbridge/demos/aiac-github-issue/demo.md
  • authbridge/demos/aiac-github-issue/k8s/configmaps.yaml
  • authbridge/demos/aiac-github-issue/k8s/git-issue-agent-deployment.yaml
  • authbridge/demos/aiac-github-issue/k8s/github-tool-deployment.yaml
  • authbridge/demos/aiac-github-issue/keycloak_ops/__init__.py
  • authbridge/demos/aiac-github-issue/keycloak_ops/apply_policy.py
  • authbridge/demos/aiac-github-issue/keycloak_ops/delete_policy.py
  • authbridge/demos/aiac-github-issue/keycloak_ops/export_config.py
  • authbridge/demos/aiac-github-issue/policies/permissive_policy.txt
  • authbridge/demos/aiac-github-issue/policies/regular_policy.txt
  • authbridge/demos/aiac-github-issue/requirements.txt
  • authbridge/demos/aiac-github-issue/setup_keycloak.py

Comment on lines +266 to +267
if structural_errors and retry_count < max_retries:
return {**state, "errors": structural_errors, "validation_passed": False, "retry_count": retry_count + 1}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Retry budget is off by one and can skip the final allowed retry.

Line 267 increments retry_count before routing, but Line 313 checks < max_retries. With max_retries=1, the first failure sets retry_count=1 and routing stops immediately (no retry).

Suggested fix
-    if not validation_passed and retry_count < max_retries:
+    if not validation_passed and retry_count <= max_retries:
         print(f"\n⚠️  Validation failed (attempt {retry_count}/{max_retries}). Retrying from parse_and_extract...")

Also applies to: 313-314

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/agent/graph.py` around lines
266 - 267, The retry logic is off-by-one: don't increment retry_count before
testing the budget; update the conditional to check whether retry_count + 1 <=
max_retries before returning a state with retry_count + 1 (i.e., replace the
current "if structural_errors and retry_count < max_retries" with a check that
ensures the incremented count is still within budget), and make the same change
for the analogous check around retry_count usage at the later block (the code
around retry_count and max_retries at the other occurrence).

Comment on lines +10 to +12
endpoint: <YOUR END POINT HERE>
api_key: <YOUR API KEY HERE>
temperature: 0.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use ${ENV_VAR} placeholders instead of literal <...> tokens.

These fields are currently template text, not env-expandable values. Please switch to ${ENV_VAR} format so runtime/env substitution works consistently with other AuthBridge YAML configs.

As per coding guidelines, authbridge/**/*.{yaml,yml} files must support ${ENV_VAR} expansion.

Also applies to: 21-23, 32-34, 43-45, 54-56

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/config/llm_conf.yaml.TEMPLATE`
around lines 10 - 12, Replace literal template tokens with env-expandable
placeholders: change the endpoint, api_key and temperature values in this LLM
config template from the current "<...>" text to ${ENV_VAR} style placeholders
(e.g. use ${LLM_ENDPOINT}, ${LLM_API_KEY}, ${LLM_TEMPERATURE} or other
project-standard names) so runtime/env substitution works; apply the same
replacement for the repeated blocks referenced (the occurrences for
endpoint/api_key/temperature at the other ranges) and ensure the YAML values
remain unquoted or appropriately quoted per YAML rules to allow env expansion.

Source: Coding guidelines

Comment thread authbridge/demos/github-issue/aiac/aiac_agent/utils/validators.py
Comment thread authbridge/demos/aiac-github-issue/aiac.env Outdated
# Optional 'secret' parameter: if not provided, Keycloak will auto-generate the client secret
clients:
- client_id: "kagenti"
secret: "demo-ui-secret"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Replace hardcoded client secret with env-expanded value.

Line 6 stores a plaintext secret in repo config. Switch to ${...} placeholder (and resolve it during startup parsing) to avoid committing secrets.

As per coding guidelines, authbridge/**/*.{yaml,yml} files must support ${ENV_VAR} expansion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/config.yaml` at line 6, Replace the
hardcoded value for the YAML key "secret" (currently "demo-ui-secret") with an
env-expanded placeholder like ${AUTHBRIDGE_UI_SECRET} in the config.yaml, and
update the authbridge config-loading/startup parsing logic that reads these YAML
files to perform environment variable expansion so the placeholder is resolved
at runtime; ensure the loader treats ${VAR} syntax (for keys such as "secret")
and falls back or errors if the env var is missing per existing validation
rules.

Source: Coding guidelines

Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Outdated
Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Outdated
Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Outdated
Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Outdated
Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py Outdated
@omerboehm
omerboehm requested review from huang195 and mrsabath June 9, 2026 18:33
Signed-off-by: Omer Boehm <omerboehm@gmail.com>
@omerboehm
omerboehm force-pushed the feat/aiac-github-issue-agent-poc branch from fe62c8f to bfb4fb6 Compare June 10, 2026 16:45

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

♻️ Duplicate comments (1)
authbridge/demos/aiac-github-issue/aiac_agent/utils/validators.py (1)

286-288: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not mark skipped semantic verification as a successful validation.

When transient LLM/API errors occur, returning (True, True, ...) lets the graph treat validation as passed, which can bypass retries and accept invalid policy mappings.

Suggested fix
-        if "Already borrowed" in error_msg or "BadRequestError" in error_msg:
-            return (True, True, "Verification skipped due to API rate limiting")
+        if "Already borrowed" in error_msg or "BadRequestError" in error_msg:
+            return (False, False, "Verification skipped due to API rate limiting")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/utils/validators.py` around
lines 286 - 288, The current branch that handles transient LLM/API errors
returns (True, True, ...) which incorrectly signals a successful validation;
update the conditional that checks for "Already borrowed" or "BadRequestError"
in validators.py so it does not mark the check as passed—return (False, True,
"Verification skipped due to API rate limiting") (i.e., success=False,
skipped=True) instead, keeping the same descriptive message so the system knows
the verification was skipped but not accepted as valid.
🧹 Nitpick comments (4)
authbridge/demos/aiac-github-issue/demo.md (1)

400-400: 💤 Low value

Use consistent capitalization for "GitHub tool".

For consistency with the rest of the documentation and the official product name, use "GitHub tool" with a capital H.

📝 Proposed fix
-Authbridge outbound check will exchange the token, then deny the request since the exchanged token will not include the github tool in the 'aud' claim.
+Authbridge outbound check will exchange the token, then deny the request since the exchanged token will not include the GitHub tool in the 'aud' claim.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/demo.md` at line 400, Change the lowercase
instance of "github tool" in the sentence that currently reads "Authbridge
outbound check will exchange the token, then deny the request since the
exchanged token will not include the github tool in the 'aud' claim." to "GitHub
tool" so it matches the rest of the docs and the official product naming; update
that exact string wherever it appears in this demo text.
authbridge/demos/aiac-github-issue/keycloak_ops/export_config.py (1)

211-228: ⚡ Quick win

Remove redundant KeycloakAdmin creation.

The first KeycloakAdmin instance (lines 212-218) is immediately discarded and replaced (lines 222-228). Remove the first creation.

Suggested fix
     print(f"\nConnecting to Keycloak at {KEYCLOAK_URL} ...")
-    admin = KeycloakAdmin(
-        server_url=KEYCLOAK_URL,
-        username=KEYCLOAK_ADMIN_USERNAME,
-        password=KEYCLOAK_ADMIN_PASSWORD,
-        realm_name="master",
-        user_realm_name="master",
-    )
-
-    # Switch to target realm
-    print(f"Switching to realm: {realm_name}")
     admin = KeycloakAdmin(
         server_url=KEYCLOAK_URL,
         username=KEYCLOAK_ADMIN_USERNAME,
         password=KEYCLOAK_ADMIN_PASSWORD,
         realm_name=realm_name,
         user_realm_name="master",
     )
+    print(f"Connected to realm: {realm_name}")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/keycloak_ops/export_config.py` around
lines 211 - 228, The first KeycloakAdmin instance is created and immediately
overwritten; remove the redundant creation block (the admin = KeycloakAdmin(...)
initialized with realm_name="master") so only the intended admin for the target
realm is created. Keep the connection and realm switching prints, and ensure the
remaining KeycloakAdmin instantiation uses server_url/username/password and
realm_name=realm_name (the existing admin variable and KeycloakAdmin symbol) so
there is a single, correct client for subsequent operations.
authbridge/demos/aiac-github-issue/keycloak_ops/apply_policy.py (1)

9-9: ⚡ Quick win

Use modern Python 3.12+ union syntax for type hints.

Replace Optional[Dict[str, str]] with Dict[str, str] | None per coding guidelines.

Suggested fix
-from typing import Dict, List, Optional
+from typing import Dict, List

And at line 73:

-    scope_ids: Optional[Dict[str, str]] = None,
+    scope_ids: Dict[str, str] | None = None,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/keycloak_ops/apply_policy.py` at line 9,
Replace legacy typing Optional usage with Python 3.12+ union syntax: find
occurrences of Optional[Dict[str, str]] in apply_policy.py (e.g., the type hint
used around line 73) and change them to Dict[str, str] | None; ensure you remove
Optional from the typing imports if no longer needed and keep the Dict and List
imports as required.

Source: Coding guidelines

authbridge/demos/aiac-github-issue/setup_keycloak.py (1)

350-355: ⚡ Quick win

Chain exception properly with raise ... from.

Line 353 raises a new exception but loses the original exception context. Use exception chaining.

Suggested fix
     except KeycloakPostError:
         internal_id = admin.get_client_id(client_id)
         if internal_id is None:
-            raise ValueError(f"Client '{client_id}' not found and could not be created")
+            raise ValueError(f"Client '{client_id}' not found and could not be created") from None
         print(f"  Using existing client: {client_id}")
         return internal_id
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/setup_keycloak.py` around lines 350 - 355,
The except block catching KeycloakPostError in setup_keycloak.py loses the
original exception context when raising ValueError; modify the except
KeycloakPostError handler to capture the original exception (e.g., except
KeycloakPostError as err) and re-raise the ValueError using exception chaining
(raise ValueError(f"Client '{client_id}' not found and could not be created")
from err) while keeping the existing check using admin.get_client_id and the
print/return of internal_id.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@authbridge/demos/aiac-github-issue/aiac_agent/utils/validators.py`:
- Line 10: Update the type annotations in validate_policy_structure and
verify_policy_semantics to use Python 3.12 built-in generics (e.g., dict[...,
...], list[...], tuple[...]) instead of typing.Dict/typing.List/typing.Tuple,
and remove the now-unused Dict/List/Tuple imports from the module; ensure
function signatures and any internal variable annotations referencing
Dict/List/Tuple are replaced with the corresponding built-in generic forms to
keep types modern and PEP-compliant.

In `@authbridge/demos/aiac-github-issue/aiac_cli.py`:
- Around line 94-115: The code prints errors when result["success"] is False but
then continues and prints parsed mappings and allows run_full_pipeline to
proceed; change the failure branch in aiac_cli.py so that after printing the
error messages (from result["errors"]) it stops further processing by raising an
exception (e.g., RuntimeError or SystemExit) or returning a failure indicator
that run_full_pipeline can detect, and ensure the parsed-scopes printing block
and builder.save_policy call only run when result["success"] is True; reference
the result dict, the error loop, and run_full_pipeline to locate where to stop
execution.

In `@authbridge/demos/aiac-github-issue/demo.md`:
- Line 396: Update the typo in the sentence "The user 'alice' is allowed to send
requests to the git issue agent, how ever the agent is not allowed to invoke the
githb tool." by replacing "githb tool" with "github tool" so it reads "...invoke
the github tool."; locate the exact string in demo.md to edit the word "githb"
to "github" (preserve surrounding punctuation and casing).
- Around line 108-123: The markdown demo has an unclosed bash code block in the
section starting with the ```bash fence in the "Step 1: Environment Setup" (the
block containing the python venv creation and pip install commands); close that
code fence by adding the missing triple backticks immediately after the last
command (`pip install -r requirements.txt`) and before the "### Step 2:" heading
so the markdown renders correctly.
- Line 835: The markdown expected-output code block is missing its closing
triple backticks; add a line containing only triple backticks (```) immediately
before the "### Step 17: Reset Realm (Optional)" heading to close the code block
and restore proper markdown rendering.

---

Duplicate comments:
In `@authbridge/demos/aiac-github-issue/aiac_agent/utils/validators.py`:
- Around line 286-288: The current branch that handles transient LLM/API errors
returns (True, True, ...) which incorrectly signals a successful validation;
update the conditional that checks for "Already borrowed" or "BadRequestError"
in validators.py so it does not mark the check as passed—return (False, True,
"Verification skipped due to API rate limiting") (i.e., success=False,
skipped=True) instead, keeping the same descriptive message so the system knows
the verification was skipped but not accepted as valid.

---

Nitpick comments:
In `@authbridge/demos/aiac-github-issue/demo.md`:
- Line 400: Change the lowercase instance of "github tool" in the sentence that
currently reads "Authbridge outbound check will exchange the token, then deny
the request since the exchanged token will not include the github tool in the
'aud' claim." to "GitHub tool" so it matches the rest of the docs and the
official product naming; update that exact string wherever it appears in this
demo text.

In `@authbridge/demos/aiac-github-issue/keycloak_ops/apply_policy.py`:
- Line 9: Replace legacy typing Optional usage with Python 3.12+ union syntax:
find occurrences of Optional[Dict[str, str]] in apply_policy.py (e.g., the type
hint used around line 73) and change them to Dict[str, str] | None; ensure you
remove Optional from the typing imports if no longer needed and keep the Dict
and List imports as required.

In `@authbridge/demos/aiac-github-issue/keycloak_ops/export_config.py`:
- Around line 211-228: The first KeycloakAdmin instance is created and
immediately overwritten; remove the redundant creation block (the admin =
KeycloakAdmin(...) initialized with realm_name="master") so only the intended
admin for the target realm is created. Keep the connection and realm switching
prints, and ensure the remaining KeycloakAdmin instantiation uses
server_url/username/password and realm_name=realm_name (the existing admin
variable and KeycloakAdmin symbol) so there is a single, correct client for
subsequent operations.

In `@authbridge/demos/aiac-github-issue/setup_keycloak.py`:
- Around line 350-355: The except block catching KeycloakPostError in
setup_keycloak.py loses the original exception context when raising ValueError;
modify the except KeycloakPostError handler to capture the original exception
(e.g., except KeycloakPostError as err) and re-raise the ValueError using
exception chaining (raise ValueError(f"Client '{client_id}' not found and could
not be created") from err) while keeping the existing check using
admin.get_client_id and the print/return of internal_id.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 50631262-b2b6-4d98-9622-811ba122c3bc

📥 Commits

Reviewing files that changed from the base of the PR and between fe62c8f and bfb4fb6.

📒 Files selected for processing (29)
  • authbridge/demos/aiac-github-issue/aiac.env
  • authbridge/demos/aiac-github-issue/aiac_agent/__init__.py
  • authbridge/demos/aiac-github-issue/aiac_agent/agent/__init__.py
  • authbridge/demos/aiac-github-issue/aiac_agent/agent/graph.py
  • authbridge/demos/aiac-github-issue/aiac_agent/agent/state.py
  • authbridge/demos/aiac-github-issue/aiac_agent/config/__init__.py
  • authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py
  • authbridge/demos/aiac-github-issue/aiac_agent/config/constants.py
  • authbridge/demos/aiac-github-issue/aiac_agent/config/llm_conf.yaml.TEMPLATE
  • authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py
  • authbridge/demos/aiac-github-issue/aiac_agent/prompts/__init__.py
  • authbridge/demos/aiac-github-issue/aiac_agent/prompts/prompt_builder.py
  • authbridge/demos/aiac-github-issue/aiac_agent/utils/__init__.py
  • authbridge/demos/aiac-github-issue/aiac_agent/utils/parsers.py
  • authbridge/demos/aiac-github-issue/aiac_agent/utils/validators.py
  • authbridge/demos/aiac-github-issue/aiac_cli.py
  • authbridge/demos/aiac-github-issue/config.yaml
  • authbridge/demos/aiac-github-issue/demo.md
  • authbridge/demos/aiac-github-issue/k8s/configmaps.yaml
  • authbridge/demos/aiac-github-issue/k8s/git-issue-agent-deployment.yaml
  • authbridge/demos/aiac-github-issue/k8s/github-tool-deployment.yaml
  • authbridge/demos/aiac-github-issue/keycloak_ops/__init__.py
  • authbridge/demos/aiac-github-issue/keycloak_ops/apply_policy.py
  • authbridge/demos/aiac-github-issue/keycloak_ops/delete_policy.py
  • authbridge/demos/aiac-github-issue/keycloak_ops/export_config.py
  • authbridge/demos/aiac-github-issue/policies/permissive_policy.txt
  • authbridge/demos/aiac-github-issue/policies/regular_policy.txt
  • authbridge/demos/aiac-github-issue/requirements.txt
  • authbridge/demos/aiac-github-issue/setup_keycloak.py
✅ Files skipped from review due to trivial changes (4)
  • authbridge/demos/aiac-github-issue/aiac_agent/utils/init.py
  • authbridge/demos/aiac-github-issue/aiac_agent/init.py
  • authbridge/demos/aiac-github-issue/policies/regular_policy.txt
  • authbridge/demos/aiac-github-issue/aiac.env
🚧 Files skipped from review as they are similar to previous changes (11)
  • authbridge/demos/aiac-github-issue/aiac_agent/config/constants.py
  • authbridge/demos/aiac-github-issue/policies/permissive_policy.txt
  • authbridge/demos/aiac-github-issue/aiac_agent/config/llm_conf.yaml.TEMPLATE
  • authbridge/demos/aiac-github-issue/aiac_agent/prompts/prompt_builder.py
  • authbridge/demos/aiac-github-issue/k8s/configmaps.yaml
  • authbridge/demos/aiac-github-issue/aiac_agent/config/config_utils.py
  • authbridge/demos/aiac-github-issue/aiac_agent/agent/state.py
  • authbridge/demos/aiac-github-issue/config.yaml
  • authbridge/demos/aiac-github-issue/aiac_agent/utils/parsers.py
  • authbridge/demos/aiac-github-issue/aiac_agent/config/llm_config.py
  • authbridge/demos/aiac-github-issue/aiac_agent/agent/graph.py

"""

import re
from typing import Any, Dict, List, Tuple

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="authbridge/demos/aiac-github-issue/aiac_agent/utils/validators.py"

# Basic sanity: ensure file exists and show key regions with line numbers
ls -l "$FILE"
echo "---- imports + around line 10 ----"
sed -n '1,40p' "$FILE" | cat -n

echo "---- around lines 80-120 (covers 99-105) ----"
sed -n '80,130p' "$FILE" | cat -n

echo "---- find typing usage in this file ----"
rg -n "from typing import|typing\.Dict|typing\.List|typing\.Tuple|\bDict\[|\bList\[|\bTuple\[" "$FILE" || true

echo "---- show function signatures that contain Dict/List/Tuple ----"
rg -n "def [a-zA-Z0-9_]+\([^)]*(Dict|List|Tuple)\b" "$FILE" || true

Repository: kagenti/kagenti-extensions

Length of output: 4788


Switch this module to Python 3.12 built-in generics (dict/list/tuple)
authbridge/demos/aiac-github-issue/aiac_agent/utils/validators.py still uses typing.Dict/List/Tuple in validate_policy_structure (lines 19-23) and verify_policy_semantics (lines 102-105); update signatures to dict[...]/list[...]/tuple[...] and remove the unused Dict/List/Tuple imports.

Suggested update
-from typing import Any, Dict, List, Tuple
+from typing import Any
@@
 def validate_policy_structure(
-    policy: Dict[str, Any],
-    realm_roles: List[Dict[str, str]],
-    client_names: List[str],
-    client_roles_map: Dict[str, List[Dict[str, str]]],
-) -> List[str]:
+    policy: dict[str, Any],
+    realm_roles: list[dict[str, str]],
+    client_names: list[str],
+    client_roles_map: dict[str, list[dict[str, str]]],
+) -> list[str]:
@@
 def verify_policy_semantics(
     state: PolicyState,
     llm: BaseChatModel,
-    client_roles_map: Dict[str, List[Dict[str, str]]],
-    client_audience_targets: Dict[str, List[str]],
+    client_roles_map: dict[str, list[dict[str, str]]],
+    client_audience_targets: dict[str, list[str]],
     verbose: bool = True,
-) -> Tuple[bool, bool, str]:
+) -> tuple[bool, bool, str]:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_agent/utils/validators.py` at line
10, Update the type annotations in validate_policy_structure and
verify_policy_semantics to use Python 3.12 built-in generics (e.g., dict[...,
...], list[...], tuple[...]) instead of typing.Dict/typing.List/typing.Tuple,
and remove the now-unused Dict/List/Tuple imports from the module; ensure
function signatures and any internal variable annotations referencing
Dict/List/Tuple are replaced with the corresponding built-in generic forms to
keep types modern and PEP-compliant.

Source: Coding guidelines

Comment on lines +94 to +115
if result["success"]:
print("✓ Access rules generated successfully!\n")
print("Generated YAML:")
print("-" * 80)
print(result["yaml_output"])
print("-" * 80)
builder.save_policy(result["yaml_output"], output_file)
else:
print("✗ Policy generation failed with errors:")
for error in result["errors"]:
print(f" - {error}")

print("\n" + "=" * 80)
print("Parsed Role-to-Client-Role Mappings:")
print("=" * 80)
for role_mapping in result["parsed_scopes"]:
realm_role = role_mapping["role"]
client_roles = role_mapping.get("client_roles", [])
print(f" {realm_role}:")
for cr in client_roles:
print(f" - {cr['client']}: {cr['role']}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Silent failure allows run_full_pipeline to proceed with invalid policy.

When result["success"] is False, the function prints errors but does not raise an exception or return a failure indicator. The caller run_full_pipeline (line 167-172) expects to catch exceptions to detect failure, so it will incorrectly print "Policy generated successfully" and proceed to delete old policies and apply the (failed) new one.

Proposed fix
     if result["success"]:
         print("✓ Access rules generated successfully!\n")
         print("Generated YAML:")
         print("-" * 80)
         print(result["yaml_output"])
         print("-" * 80)
         builder.save_policy(result["yaml_output"], output_file)
     else:
         print("✗ Policy generation failed with errors:")
         for error in result["errors"]:
             print(f"  - {error}")
+        raise RuntimeError(f"Policy generation failed: {result['errors']}")
 
     print("\n" + "=" * 80)
     print("Parsed Role-to-Client-Role Mappings:")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/aiac_cli.py` around lines 94 - 115, The
code prints errors when result["success"] is False but then continues and prints
parsed mappings and allows run_full_pipeline to proceed; change the failure
branch in aiac_cli.py so that after printing the error messages (from
result["errors"]) it stops further processing by raising an exception (e.g.,
RuntimeError or SystemExit) or returning a failure indicator that
run_full_pipeline can detect, and ensure the parsed-scopes printing block and
builder.save_policy call only run when result["success"] is True; reference the
result dict, the error loop, and run_full_pipeline to locate where to stop
execution.

Comment thread authbridge/demos/github-issue/demo-aiac.md

⚠️ NOTE ⚠️ - make sure alice|alice123 (bob/bob123) appear in keycloak users under 'kagenti' realm (reset password if needed and make sure temporary password is set)

The user 'alice' is allowed to send requests to the git issue agent, how ever the agent is not allowed to invoke the githb tool.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix typo: "githb tool" → "github tool".

📝 Proposed fix
-The user 'alice' is allowed to send requests to the git issue agent, how ever the agent is not allowed to invoke the githb tool.
+The user 'alice' is allowed to send requests to the git issue agent, however the agent is not allowed to invoke the github tool.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The user 'alice' is allowed to send requests to the git issue agent, how ever the agent is not allowed to invoke the githb tool.
The user 'alice' is allowed to send requests to the git issue agent, however the agent is not allowed to invoke the github tool.
🧰 Tools
🪛 LanguageTool

[grammar] ~396-~396: Ensure spelling is correct
Context: ... the agent is not allowed to invoke the githb tool. First, get a valid token from Key...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/aiac-github-issue/demo.md` at line 396, Update the typo in
the sentence "The user 'alice' is allowed to send requests to the git issue
agent, how ever the agent is not allowed to invoke the githb tool." by replacing
"githb tool" with "github tool" so it reads "...invoke the github tool."; locate
the exact string in demo.md to edit the word "githb" to "github" (preserve
surrounding punctuation and casing).

Comment thread authbridge/demos/github-issue/demo-aiac.md
…oak.py

Signed-off-by: Omer Boehm <omerboehm@gmail.com>

@huang195 huang195 left a comment

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.

Review Summary

Well-structured POC — YAML loads are all safe_load, no shell injection, TLS verification never disabled, resource limits set on both deployments, and there's a real structural allowlist (validate_policy_structure with bounded retry). Nicely done, and good catch self-fixing the credential-logging in commit 2.

The crux of this kind of demo is the LLM → IdP trust path, and that's where the suggestions cluster: the structural allowlist is the right idea, but it's only enforced in the agent graph — not re-checked at apply time — and two spots fail open (the semantic verifier on exception, and the swallowed apply error). The raw NL description is also interpolated straight into both the generation and verification prompts, so treat it as untrusted input and keep the structural allowlist authoritative over the LLM's verdict. None of this blocks a demo, but these become must-fix if AIAC ever graduates past POC.

Smaller notes (not inlined): no tests for validate_policy_structure/load_access_control_policy (high-value given they gate IdP writes); no HTTP timeout on KeycloakAdmin; and a # Made with Bob trailer left in ~12 source files — strip those.

Areas reviewed: Python (agent, keycloak ops, CLI), k8s, env/config, docs · Commits: 2, signed-off ✅ · CI: all green

No code blockers for a POC demo — approving, with the LLM-to-IdP guardrails flagged for follow-up.

Assisted-By: Claude Code

error_msg = str(e)
# Handle "Already borrowed" errors gracefully
if "Already borrowed" in error_msg or "BadRequestError" in error_msg:
return (True, True, "Verification skipped due to API rate limiting")

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.

suggestion (security) — The semantic verifier fails open here: on Already borrowed/BadRequestError it returns (True, True, "Verification skipped…"), i.e. "policy is correct," silently skipping validation. A validator that gates privileged Keycloak role grants should fail closed — treat an unverifiable result as not-validated rather than assuming success.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

add_client_role_to_realm_role_composite(admin, realm, user_role, client_id, role_name)
print(f" ✓ Added client role '{client_name}.{role_name}' to realm role '{user_role}'")
except Exception as e:
print(f" ℹ Client role '{client_name}.{role_name}' already in composite or error: {e}")

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.

suggestion (security) — The role allowlist is enforced only in the agent graph (validators.validate_policy_structure), not here at write time — load_access_control_policy validates shape only and trusts whatever YAML it's handed. And this except swallows a failed get_client_role as "…already in composite or error", so an invalid/over-broad mapping is logged as benign instead of aborting. Re-check roles against the allowlist / live client roles at apply time and fail (non-zero) on error, so a regenerated or hand-edited policy can't bypass the agent-side gate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

Comment thread authbridge/demos/github-issue/aiac/aiac_cli.py
Comment thread authbridge/demos/aiac-github-issue/aiac.env Outdated
Comment thread authbridge/demos/aiac-github-issue/k8s/git-issue-agent-deployment.yaml Outdated
@huang195

Copy link
Copy Markdown
Member

@omerboehm we can merge now or would you like to make some changes based on the reviews? Will wait until next week in case you want to make some changes.

@pdettori pdettori left a comment

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.

Review Summary (architecture / big-picture)

Approving — trusted-maintainer POC, CI green, and the security items (CodeQL clear-text logging, CodeRabbit retry/validator/template findings) are already covered by other reviewers, so this review focuses only on how the demo fits the existing AuthBridge demo suite. Nothing below blocks merge, but #1 and #2 are worth addressing before this is promoted beyond POC.

1. Duplicates the existing github-issue demo wholesale. Diffing the copied files against authbridge/demos/github-issue/:

  • k8s/configmaps.yaml — byte-identical
  • k8s/github-tool-deployment.yaml — byte-identical
  • k8s/git-issue-agent-deployment.yaml — differs only in (stale) comments
  • setup_keycloak.py — forked & expanded (491→845 lines; ~103 shared)

AIAC is conceptually an add-on (an admin-side policy generator) to the github-issue scenario, not a separate stack. Copying the agent/tool/Keycloak setup creates a second copy that will drift. Consider the weather-agent pattern (beginner vs *-advanced sharing the same images/manifests): reuse the github-issue manifests + setup_keycloak.py and contribute only the AIAC-specific pieces (aiac_agent/, keycloak_ops/, aiac_cli.py, policies/, extra roles).

2. Copied from a pre-#411 version → ships stale architecture docs. The duplicated agent deployment and demo.md describe the removed per-component sidecars (authbridge-unified, kagenti-client-registration, standalone spiffe-helper). demos/README.md explicitly documents these were removed after #411 in favor of one combined sidecar. The existing github-issue demo was already updated; this copy was not — a direct consequence of copying rather than reusing. See inline comments.

3. Not registered in demos/README.md. Every other demo appears in the AuthBridge Demos table + recommended-path; this one is absent, so it's undiscoverable and breaks the suite convention.

4. No reproducible-deploy tooling. The comparable LLM-judge demo (ibac) ships a Makefile + scripts (make show-result). AIAC is manual CLI steps only — inconsistent with the suite's automated deploy/verify convention.

5. (Soft) keycloak_ops/ overlaps with existing keycloak_sync.py. Both are Keycloak-admin clients (export realm, manage roles/clients). Different purpose, but the admin-client/export plumbing could likely share a helper instead of a third independent implementation.

Assisted-By: Claude Code

Comment thread authbridge/demos/aiac-github-issue/k8s/git-issue-agent-deployment.yaml Outdated
Comment thread authbridge/demos/aiac-github-issue/demo.md Outdated
- validators.py: fail closed on transient API errors instead of returning
  (True, True) which silently let unverified policies pass
- apply_policy.py: pre-validate all policy roles against live Keycloak
  state before any writes; remove broad exception swallowing so real
  errors surface immediately
- aiac_cli.py: add --yes/-y flag and a confirmation gate between policy
  generation and the destructive delete/apply steps; default is to prompt
- aiac.env.TEMPLATE: add template file following the .TEMPLATE convention
- .gitignore: ignore aiac.env (runtime credentials) and
  aiac_agent/config/llm_conf.yaml (runtime LLM config with API keys)

Signed-off-by: Omer Boehm <omerboehm@gmail.com>
@huang195 huang195 changed the title feat: Add AI-assisted GitHub issue agent POC Feat: Add AI-assisted GitHub issue agent POC Jun 18, 2026
Addresses PR review: AIAC demo lacked reproducible-deploy tooling
consistent with the suite convention (ibac, finance-sparc, echo all
ship a Makefile).

Adds:
- authbridge/demos/github-issue/aiac/Makefile — setup / apply-policy /
  apply-permissive / show-result / reset targets with preflight checks
  (Python, aiac.env, llm_conf.yaml, Keycloak reachability)
- authbridge/demos/github-issue/aiac/scripts/show-result.py — queries
  Keycloak admin API for live composite-role mappings and prints the
  last generated policy file for side-by-side comparison

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Omer Boehm <omerboehm@gmail.com>
…k and requirements

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Omer Boehm <omerboehm@gmail.com>
@omerboehm
omerboehm force-pushed the feat/aiac-github-issue-agent-poc branch from 540b61c to 20dad3b Compare June 18, 2026 14:41
Signed-off-by: Omer Boehm <omerboehm@gmail.com>

@huang195 huang195 left a comment

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.

Clean, self-contained POC — a LangGraph agent that turns natural-language policy descriptions into Keycloak composite-role mappings, with a CLI, Makefile, and walkthrough doc. Security posture is good for a demo: no subprocess/eval/exec, all YAML via safe_load, no verify=False, no leaked real secrets, and the "avoid clear-text credential logging" fix in setup_keycloak.py checks out (status labels only, never the secret value). New deps are legitimate with no typosquats, and apply_policy.py validates policy roles against live Keycloak before writing.

Findings are almost entirely documentation accuracy. The one that actually breaks the walkthrough is the cleanup command missing the -rbac flag (inline) — worth fixing since the demo's value is the documented steps working end to end. The rest are minor (two stale script-name references, dependency pinning, a labeled demo secret, leftover marker comments). None block merge, so approving with these as follow-ups.

Author: omerboehm (MEMBER — maintainer) · Areas reviewed: Python, Makefile, Docs, dependencies, secrets · Agent/IDE config (.claude/.vscode): none · Commits: 7 (6 signed-off; 1 merge commit) · CI: passing

Assisted-By: Claude Code

Comment thread authbridge/demos/github-issue/demo-aiac.md Outdated
Comment thread authbridge/demos/github-issue/demo-aiac.md Outdated
Comment thread authbridge/demos/github-issue/aiac/keycloak_ops/export_config.py Outdated
Comment thread authbridge/requirements.txt Outdated
Comment thread authbridge/demos/github-issue/aiac/config.yaml Outdated
Comment thread authbridge/demos/github-issue/aiac/aiac_agent/config/config_utils.py Outdated
Signed-off-by: Omer Boehm <omerboehm@gmail.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
authbridge/demos/github-issue/demo-aiac.md (1)

445-447: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Decode the JWT payload correctly.

This snippet keeps the signature segment (-f2-) and then feeds a JWT payload to plain base64 -d, so it will fail on real tokens. Decode only the payload segment with a base64url-aware helper.

🛠️ Proposed fix
-echo $ALICE_TOKEN | cut -d. -f2- | base64 -d | jq .
+ALICE_TOKEN="$ALICE_TOKEN" python - <<'PY'
+import base64
+import json
+import os
+
+payload = os.environ["ALICE_TOKEN"].split(".")[1]
+payload += "=" * (-len(payload) % 4)
+print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))
+PY
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/github-issue/demo-aiac.md` around lines 445 - 447, The JWT
decoding command is extracting both the payload and signature segments (using
cut -f2-) and then attempting standard base64 decoding, which fails on real
tokens with base64url encoding. Fix the cut command to extract only the payload
segment by changing the field selection from -f2- to -f2, and ensure the base64
decoding handles the base64url format correctly by either adding proper padding
or using a base64url-aware decoder instead of plain base64 -d.
♻️ Duplicate comments (1)
authbridge/requirements.txt (1)

9-9: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pin python-dotenv to an exact version.

Line 9 uses python-dotenv>=1, which breaks reproducibility and violates the repo’s requirements.txt pinning rule.

Proposed fix
-python-dotenv>=1
+python-dotenv==<tested-version>
#!/bin/bash
# Verify non-pinned dependency specifiers in authbridge/requirements.txt
awk '
  /^[[:space:]]*`#/` || /^[[:space:]]*$/ { next }
  $0 !~ /==/ { print NR ":" $0 }
' authbridge/requirements.txt

As per coding guidelines, **/requirements.txt: Python dependencies must be version-pinned in requirements.txt (e.g., python-keycloak==5.3.1).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/requirements.txt` at line 9, The dependency python-dotenv on line
9 of authbridge/requirements.txt uses a version range specifier (>=1) instead of
pinning to an exact version. Replace python-dotenv>=1 with python-dotenv==
followed by the exact version number (for example, python-dotenv==1.0.0) to
comply with the repository's requirement that all Python dependencies must be
version-pinned using the == operator for reproducibility.

Source: Coding guidelines

🧹 Nitpick comments (2)
authbridge/demos/github-issue/setup_keycloak.py (1)

863-875: ⚡ Quick win

Chain exception to preserve context.

When re-raising as ValueError, chain the original exception to preserve the stack trace for debugging.

 def create_client_idempotent(admin: KeycloakAdmin, payload: dict) -> str:
     """Create a client or return existing internal ID."""
     client_id = payload["clientId"]
     try:
         internal_id = admin.create_client(payload)
         print(f"  Created client: {client_id}")
         return internal_id
-    except KeycloakPostError:
+    except KeycloakPostError as e:
         internal_id = admin.get_client_id(client_id)
         if internal_id is None:
-            raise ValueError(f"Client '{client_id}' not found and could not be created")
+            raise ValueError(f"Client '{client_id}' not found and could not be created") from e
         print(f"  Using existing client: {client_id}")
         return internal_id
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/github-issue/setup_keycloak.py` around lines 863 - 875, In
the create_client_idempotent function, when raising the ValueError because the
client was not found and could not be created, chain the exception using the
from keyword to preserve the original KeycloakPostError context. This ensures
the stack trace from the initial exception is available for debugging instead of
being lost when the ValueError is raised.

Source: Linters/SAST tools

authbridge/demos/github-issue/aiac/scripts/show-result.py (1)

49-59: ⚡ Quick win

Tighten the exception handlers.

These except Exception blocks will also swallow unexpected bugs, which makes this script harder to debug. Please narrow the handlers to the failures you actually expect, and let everything else fail fast.

Also applies to: 70-77

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@authbridge/demos/github-issue/aiac/scripts/show-result.py` around lines 49 -
59, The exception handler in the KeycloakAdmin initialization is too broad by
catching all Exception types, which can hide unexpected bugs and make debugging
harder. Replace the broad except Exception clause with specific exception types
that are actually expected to be raised by the KeycloakAdmin constructor during
connection failures or authentication errors. Let unexpected exceptions fail
fast by not catching them. Apply the same fix to the other similar broad
exception handler mentioned in the file around lines 70-77.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@authbridge/demos/github-issue/aiac/Makefile`:
- Around line 67-70: The preflight check in the Python import validation command
is incomplete and only checks for dotenv, yaml, keycloak, and langgraph. Add the
three missing required dependencies—langchain-core, langchain-openai, and
pydantic—to the import list in the same Python command (in the @$(PYTHON) -c
line) so that the preflight check will properly fail if any of these
dependencies are not installed, preventing downstream failures when apply-policy
runs.

In `@authbridge/demos/github-issue/aiac/scripts/show-result.py`:
- Around line 88-95: In the show-result.py file where policy files are
retrieved, the sorted() call that processes the glob result is sorting files
lexicographically by filename rather than by modification time. Update the
sorted() function call to include a key parameter that sorts files by their
modification time (st_mtime) to ensure the most recently produced policy file is
selected, not just the lexicographically last filename. This can be done by
sorting in reverse order (most recent first) and selecting the first element, or
sorting in normal order and selecting the last element with the appropriate key
function.

In `@authbridge/demos/github-issue/demo-aiac.md`:
- Around line 219-221: The plain fenced code blocks in the demo-aiac.md file are
missing language identifiers and causing markdownlint violations. Replace all
plain triple backtick fences that contain output text (at lines 219-221 and
261-264) with triple backticks followed by the `txt` language identifier to
properly mark them as text output blocks. This ensures the documentation remains
lint-clean while maintaining proper formatting for the output snippets.

In `@authbridge/demos/github-issue/setup_keycloak.py`:
- Around line 602-606: The error message in the ValueError raised when checking
for missing environment variables references the wrong configuration file path.
Update the error message string to correctly reference the actual configuration
file being used (aiac/aiac.env) instead of the generic .env file name. This will
ensure users troubleshooting credential issues are directed to the correct file
location.
- Around line 1355-1356: The direct dictionary access main_config["users"] will
raise a KeyError if the "users" key is missing from the configuration. Replace
this direct access with the defensive .get() method that provides a safe default
value (such as an empty dictionary or list depending on what create_users
expects), consistent with the pattern used elsewhere in the codebase like at
line 1340. Update the line where users_config is assigned to use
main_config.get("users", <appropriate_default>) to handle missing configuration
gracefully.

---

Outside diff comments:
In `@authbridge/demos/github-issue/demo-aiac.md`:
- Around line 445-447: The JWT decoding command is extracting both the payload
and signature segments (using cut -f2-) and then attempting standard base64
decoding, which fails on real tokens with base64url encoding. Fix the cut
command to extract only the payload segment by changing the field selection from
-f2- to -f2, and ensure the base64 decoding handles the base64url format
correctly by either adding proper padding or using a base64url-aware decoder
instead of plain base64 -d.

---

Duplicate comments:
In `@authbridge/requirements.txt`:
- Line 9: The dependency python-dotenv on line 9 of authbridge/requirements.txt
uses a version range specifier (>=1) instead of pinning to an exact version.
Replace python-dotenv>=1 with python-dotenv== followed by the exact version
number (for example, python-dotenv==1.0.0) to comply with the repository's
requirement that all Python dependencies must be version-pinned using the ==
operator for reproducibility.

---

Nitpick comments:
In `@authbridge/demos/github-issue/aiac/scripts/show-result.py`:
- Around line 49-59: The exception handler in the KeycloakAdmin initialization
is too broad by catching all Exception types, which can hide unexpected bugs and
make debugging harder. Replace the broad except Exception clause with specific
exception types that are actually expected to be raised by the KeycloakAdmin
constructor during connection failures or authentication errors. Let unexpected
exceptions fail fast by not catching them. Apply the same fix to the other
similar broad exception handler mentioned in the file around lines 70-77.

In `@authbridge/demos/github-issue/setup_keycloak.py`:
- Around line 863-875: In the create_client_idempotent function, when raising
the ValueError because the client was not found and could not be created, chain
the exception using the from keyword to preserve the original KeycloakPostError
context. This ensures the stack trace from the initial exception is available
for debugging instead of being lost when the ValueError is raised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ebf6f049-9ad9-4526-8fd5-c2adcb7edc64

📥 Commits

Reviewing files that changed from the base of the PR and between 1239275 and c0b151c.

📒 Files selected for processing (29)
  • authbridge/demos/github-issue/aiac/.gitignore
  • authbridge/demos/github-issue/aiac/Makefile
  • authbridge/demos/github-issue/aiac/aiac.env.TEMPLATE
  • authbridge/demos/github-issue/aiac/aiac_agent/__init__.py
  • authbridge/demos/github-issue/aiac/aiac_agent/agent/__init__.py
  • authbridge/demos/github-issue/aiac/aiac_agent/agent/graph.py
  • authbridge/demos/github-issue/aiac/aiac_agent/agent/state.py
  • authbridge/demos/github-issue/aiac/aiac_agent/config/__init__.py
  • authbridge/demos/github-issue/aiac/aiac_agent/config/config_utils.py
  • authbridge/demos/github-issue/aiac/aiac_agent/config/constants.py
  • authbridge/demos/github-issue/aiac/aiac_agent/config/llm_conf.yaml.TEMPLATE
  • authbridge/demos/github-issue/aiac/aiac_agent/config/llm_config.py
  • authbridge/demos/github-issue/aiac/aiac_agent/prompts/__init__.py
  • authbridge/demos/github-issue/aiac/aiac_agent/prompts/prompt_builder.py
  • authbridge/demos/github-issue/aiac/aiac_agent/utils/__init__.py
  • authbridge/demos/github-issue/aiac/aiac_agent/utils/parsers.py
  • authbridge/demos/github-issue/aiac/aiac_agent/utils/validators.py
  • authbridge/demos/github-issue/aiac/aiac_cli.py
  • authbridge/demos/github-issue/aiac/config.yaml
  • authbridge/demos/github-issue/aiac/keycloak_ops/__init__.py
  • authbridge/demos/github-issue/aiac/keycloak_ops/apply_policy.py
  • authbridge/demos/github-issue/aiac/keycloak_ops/delete_policy.py
  • authbridge/demos/github-issue/aiac/keycloak_ops/export_config.py
  • authbridge/demos/github-issue/aiac/policies/permissive_policy.txt
  • authbridge/demos/github-issue/aiac/policies/regular_policy.txt
  • authbridge/demos/github-issue/aiac/scripts/show-result.py
  • authbridge/demos/github-issue/demo-aiac.md
  • authbridge/demos/github-issue/setup_keycloak.py
  • authbridge/requirements.txt
💤 Files with no reviewable changes (21)
  • authbridge/demos/github-issue/aiac/policies/permissive_policy.txt
  • authbridge/demos/github-issue/aiac/aiac_agent/config/constants.py
  • authbridge/demos/github-issue/aiac/aiac.env.TEMPLATE
  • authbridge/demos/github-issue/aiac/policies/regular_policy.txt
  • authbridge/demos/github-issue/aiac/aiac_agent/prompts/init.py
  • authbridge/demos/github-issue/aiac/aiac_agent/agent/state.py
  • authbridge/demos/github-issue/aiac/aiac_agent/init.py
  • authbridge/demos/github-issue/aiac/aiac_agent/config/llm_conf.yaml.TEMPLATE
  • authbridge/demos/github-issue/aiac/aiac_agent/utils/init.py
  • authbridge/demos/github-issue/aiac/keycloak_ops/init.py
  • authbridge/demos/github-issue/aiac/keycloak_ops/delete_policy.py
  • authbridge/demos/github-issue/aiac/aiac_agent/config/config_utils.py
  • authbridge/demos/github-issue/aiac/aiac_agent/prompts/prompt_builder.py
  • authbridge/demos/github-issue/aiac/keycloak_ops/apply_policy.py
  • authbridge/demos/github-issue/aiac/aiac_agent/utils/parsers.py
  • authbridge/demos/github-issue/aiac/aiac_agent/agent/init.py
  • authbridge/demos/github-issue/aiac/aiac_agent/config/init.py
  • authbridge/demos/github-issue/aiac/aiac_agent/config/llm_config.py
  • authbridge/demos/github-issue/aiac/aiac_cli.py
  • authbridge/demos/github-issue/aiac/aiac_agent/utils/validators.py
  • authbridge/demos/github-issue/aiac/aiac_agent/agent/graph.py

Comment thread authbridge/demos/github-issue/aiac/Makefile Outdated
Comment thread authbridge/demos/github-issue/aiac/scripts/show-result.py
Comment thread authbridge/demos/github-issue/demo-aiac.md Outdated
Comment thread authbridge/demos/github-issue/setup_keycloak.py
Comment thread authbridge/demos/github-issue/setup_keycloak.py Outdated
Signed-off-by: Omer Boehm <omerboehm@gmail.com>

@huang195 huang195 left a comment

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.

Post-approval pass — the PR is already approved, so these are all non-blocking demo cleanups, no must-fix. Reviewed the full diff with a security focus given the size.

Security: clean. No real secrets are committed — aiac.env/llm_conf.yaml ship as .TEMPLATE placeholders with the live files in .gitignore. No verify=False, shell=True, eval/exec, subprocess, or curl|bash execution. The Keycloak admin operations build URLs from Keycloak-returned IDs and pre-validate policy clients/roles against live realm state before writing. The earlier github-advanced-security clear-text-logging finding is addressed in 2f3fb54.

Three small inline nits below, plus one PR-description nit: the body's paths are stale after the "move under github-issue dir" refactor — it references demos/aiac-github-issue/ and demo.md, but the files live at demos/github-issue/aiac/ and the walkthrough is demo-aiac.md. Worth updating so the testing steps are followable.

Assisted-By: Claude Code

# Optional 'secret' parameter: if not provided, Keycloak will auto-generate the client secret
clients:
- client_id: "kagenti"
#secret: "demo-ui-secret-for-testing-do-not-use"

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.

nit (demo cleanup) — these three commented-out example client secrets (#secret: "…-for-testing-do-not-use") are inert (commented, and Keycloak auto-generates a secret when the field is absent), but committing real-looking secret literals invites copy-paste into a real config. Consider dropping them or leaving a single # secret: <optional; auto-generated if omitted> note.

"credentials": [
{
"type": "password",
"value": f"{username}123",

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.

nit (demo-scoped) — deterministic, non-temporary passwords (alice123, bob123, …) are fine for a throwaway demo realm, but worth a one-line "demo-only; never point setup_keycloak.py at a shared/prod Keycloak" note near here. Separately: the user is created with an inline credentials: [...] block just above, and then set_user_password(...) is called again at line 1228 — the second call looks redundant unless it's working around a Keycloak quirk (if so, a short comment would help).

else:
# Standard OpenAI-compatible endpoint (litellm, Ollama, etc.)
if verbose:
backend_type = "Ollama" if "ollama" in llm_config.api_key.lower() else "OpenAI-compatible"

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.

nit"ollama" in llm_config.api_key.lower() keys the backend type off the API-key value, which is fragile (an Ollama key isn't guaranteed to contain "ollama") and reads the secret for a display-only heuristic. Prefer deriving the backend from the endpoint/config field instead. The key itself is correctly wrapped in SecretStr for the actual client — good.

@huang195
huang195 merged commit c52044e into rossoctl:main Jun 19, 2026
20 checks passed
@github-project-automation github-project-automation Bot moved this from New/ToDo to Done in Rossoctl Issue Prioritization Jun 19, 2026
@omerboehm
omerboehm deleted the feat/aiac-github-issue-agent-poc branch June 28, 2026 10:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

feature: AI Access Control (AIAC) - First POC

5 participants