Skip to content

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

Open
omerboehm wants to merge 2 commits into
kagenti:mainfrom
s-and-p-team:feat/aiac-github-issue-agent-poc
Open

feat: Add AI-assisted GitHub issue agent POC#492
omerboehm wants to merge 2 commits into
kagenti:mainfrom
s-and-p-team:feat/aiac-github-issue-agent-poc

Conversation

@omerboehm

@omerboehm omerboehm commented Jun 9, 2026

Copy link
Copy Markdown

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

  • New Features

    • AI Access Control (AIAC) demo for GitHub issue agents with Keycloak integration.
    • Natural-language → validated YAML policy generation powered by an AI policy-builder.
    • CLI for generating/applying policies and end-to-end Keycloak setup automation.
    • Kubernetes manifests and provisioning scripts to deploy demo components and Keycloak realm.
  • Documentation

    • Full end-to-end demo walkthrough and setup guide added.

@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 45 minutes and 20 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ 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.

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

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

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: 8561b954-fcab-4c62-af55-e5a980ab9d58

📥 Commits

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

📒 Files selected for processing (1)
  • authbridge/demos/aiac-github-issue/setup_keycloak.py
📝 Walkthrough

Walkthrough

Adds an end-to-end AI Access Control (AIAC) demo: an LLM-driven PolicyBuilder (LangGraph) that generates validated Keycloak YAML, Keycloak export/apply/delete tooling, provisioning scripts, a CLI pipeline, Kubernetes manifests, and full demo documentation.

Changes

AI Access Control Agent + Keycloak Integration

Layer / File(s) Summary
Agent package & state, config loaders
authbridge/demos/aiac-github-issue/aiac.env, aiac_agent/__init__.py, aiac_agent/agent/__init__.py, aiac_agent/agent/state.py, aiac_agent/config/*
Defines PolicyState TypedDict, package exports, config loaders and constants, LLM config dataclass and factory with YAML/env support and lazy default LLM creation.
LLM prompting, response parsing, and policy validation
aiac_agent/prompts/*, aiac_agent/utils/parsers.py, aiac_agent/utils/validators.py
build_system_prompt / build_retry_prompt create instruction-rich prompts. extract_explanation_and_json parses LLM outputs. validate_policy_structure checks policy shape; verify_policy_semantics invokes an LLM to semantically verify the generated policy.
LangGraph workflow and PolicyBuilder orchestration
aiac_agent/agent/graph.py
LangGraph nodes: _parse_and_extract_scopes, _build_policy, _generate_yaml, _validate_policy and conditional retry _should_retry_validation. PolicyBuilder loads config, compiles the graph, and exposes generate_policy and save_policy.
Keycloak configuration export, policy application, and deletion
keycloak_ops/__init__.py, keycloak_ops/export_config.py, keycloak_ops/apply_policy.py, keycloak_ops/delete_policy.py
export_config exports clients/realm_roles/client_audience_targets/users to YAML. apply_access_control_policy adds client-role composites to realm roles. delete_access_control_policy removes composite mappings.
Keycloak realm provisioning and setup
setup_keycloak.py
Idempotent provisioning: create realm, clients, client roles, realm roles, create client scopes with audience mappers, assign default/optional scopes, map scopes to roles, create users, and support --reset-only.
CLI entry point and pipeline
aiac_cli.py, requirements.txt
aiac_cli.py supports generate (policy-only) and full pipeline (export -> generate -> delete -> apply). Colored console helpers and argument parsing. requirements.txt lists runtime dependencies.
Kubernetes manifests and runtime config
k8s/configmaps.yaml, k8s/git-issue-agent-deployment.yaml, k8s/github-tool-deployment.yaml
ConfigMaps for AuthBridge and route rules; Git issue agent Deployment with AuthBridge/SPIRE labels and LLM settings; GitHub tool Deployment with scope-based access and Keycloak validation; Services wired for both.
Demo docs, example policies, and templates
demo.md, config.yaml, policies/*, aiac_agent/config/llm_conf.yaml.TEMPLATE
Full walkthrough from setup to verification/reset, example policy texts, main demo config, and LLM config template.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • mrsabath
  • huang195

Poem

🐰 I nibble prompts and stitch a YAML song,
I pair roles and clients all day long.
LangGraph hums and Keycloak keeps the gate,
Policies blossom—neat and up to date.
Hooray for demos: hop, generate, celebrate!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.19% 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 pull request title 'feat: Add AI-assisted GitHub issue agent POC' clearly summarizes the main change: introducing a proof-of-concept demo for an AI-powered GitHub issue agent with access control policy generation capabilities.
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 on lines +283 to +288
except Exception as e:
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")
return (False, False, f"Could not verify policy: {error_msg}")

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

Do not mark semantic verification failures as successful validation.

Line 287 currently returns (True, True, ...) on certain API errors, which can let invalid policies pass final validation. In agent/graph.py this short-circuits retry/failure behavior because the validator booleans are treated as authoritative pass/fail signals.

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 transient API error; retry required")
🧰 Tools
🪛 Ruff (0.15.15)

[warning] 283-283: Do not catch blind exception: Exception

(BLE001)

🤖 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 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).

Comment on lines +3 to +4
KEYCLOAK_ADMIN_USERNAME=admin
KEYCLOAK_ADMIN_PASSWORD=admin

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

Avoid committed default admin credentials.

Lines 3-4 commit live-looking credentials (admin/admin). Even for demos, this is high-risk when manifests/scripts are reused in shared environments. Use placeholders and require runtime-provided secrets.

🤖 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.env` around lines 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.

# 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 on lines +49 to +55
def load_main_config(config_file: Path) -> Dict[str, Any]:
"""Load main configuration from YAML file."""
if not config_file.exists():
raise FileNotFoundError(f"Configuration file not found: {config_file}")

with open(config_file, "r") as f:
return yaml.safe_load(f)

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

Add ${ENV_VAR} expansion and startup mode validation when loading YAML.

load_main_config currently loads raw YAML only. This misses required env expansion support and startup validation for config mode matching.

As per coding guidelines, authbridge/**/*.{yaml,yml} configs must support ${ENV_VAR} expansion and include startup validation for mode matching.

🤖 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 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.

Source: Coding guidelines

Comment on lines +248 to +258
def delete_realm_roles(admin: KeycloakAdmin, realm_roles_config: List[str]) -> None:
print("\nDeleting realm roles:")
if not realm_roles_config:
print(" No realm roles in configuration")
return
for role_name in realm_roles_config:
try:
admin.delete_realm_role(role_name)
print(f" ✓ Deleted realm role: {role_name}")
except Exception as e:
print(f" - Realm role not found or error: {role_name} ({e})")

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

Realm-role cleanup breaks with dict-shaped role config.

delete_realm_roles assumes List[str], but this demo config uses objects ({name, description}). That causes failed deletions and undermines reset idempotency.

Suggested fix
-def delete_realm_roles(admin: KeycloakAdmin, realm_roles_config: List[str]) -> None:
+def delete_realm_roles(admin: KeycloakAdmin, realm_roles_config: List[dict[str, Any] | str]) -> None:
@@
-    for role_name in realm_roles_config:
+    for role in realm_roles_config:
+        role_name = role["name"] if isinstance(role, dict) else role
         try:
             admin.delete_realm_role(role_name)
🧰 Tools
🪛 Ruff (0.15.15)

[warning] 257-257: Do not catch blind exception: Exception

(BLE001)

🤖 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 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.

Comment thread authbridge/demos/aiac-github-issue/setup_keycloak.py
Comment on lines +446 to +465
def create_client_scopes(
admin: KeycloakAdmin,
client_ids: Dict[str, Dict[str, Any]],
) -> Dict[str, str]:
"""Create one scope per role per client. Returns {scope_name: scope_id}."""
print("\n=== Creating client scopes ===")
scope_ids: Dict[str, str] = {}
for client_name, client_info in client_ids.items():
for role in client_info["roles"]:
# Extract role name - support both dict and string formats
scope_name = role["name"] if isinstance(role, dict) else role
scope_id = create_single_client_scope(
admin,
scope_name,
client_name,
DEFAULT_SCOPE_ATTRIBUTES,
DEFAULT_MAPPER_CONFIG,
)
scope_ids[scope_name] = scope_id
return scope_ids

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 | 🏗️ Heavy lift

Scope ID map keyed only by role name can collide across clients.

scope_ids[scope_name] = scope_id overwrites entries when different clients share a role name (common with access). Downstream assignments (Lines 553/621) can bind wrong scopes and break audience isolation.

🤖 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 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.

Comment on lines +559 to +560
except Exception:
pass # Already added

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

Avoid silent except: pass in scope assignment paths.

These blocks suppress real provisioning failures (timeouts/auth/permission) and still report success. At minimum, log the exception; ideally re-raise non-idempotent cases.

Also applies to: 627-628

🧰 Tools
🪛 Ruff (0.15.15)

[error] 559-560: try-except-pass detected, consider logging the exception

(S110)


[warning] 559-559: Do not catch blind exception: Exception

(BLE001)

🤖 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 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".

Source: Linters/SAST tools

@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 on lines +108 to +123
### Step 1: Environment Setup

Create and activate a Python virtual environment:

```bash
cd kagenti-extensions/authbridge/demos/aiac-github-issue

# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate

# Install dependencies
pip install --upgrade pip
pip install -r requirements.txt


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 | 🔴 Critical | ⚡ Quick win

Critical: Missing closing backticks breaks markdown rendering.

The bash code block starting at line 112 is never closed. Add closing triple backticks after line 121 (after pip install -r requirements.txt) and before the ### Step 2: heading on line 124.

📝 Proposed fix
 # Install dependencies
 pip install --upgrade pip
 pip install -r requirements.txt
+```

 
 ### Step 2: Apply Demo ConfigMaps
🤖 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` around lines 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.


⚠️ 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).

5. Sales is now just like Tech-support ("Other personnel"):
client: github-tool
role: github-tool-aud

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

Add missing closing backticks after the expected output.

The expected output code block starting at line 817 should be closed before the heading on line 838.

📝 Proposed fix
 role: github-tool-aud
-
-
+```

 ### Step 17: Reset Realm (Optional)
🤖 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 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.

…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.

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.

realm_name=realm_name,
user_realm_name="master",
)
delete_access_control_policy(admin, realm_name, script_dir / main_config)

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.

suggestionrun_full_pipeline runs delete old policy → apply new policy against a live realm with no confirmation gate. For a demo whose thesis is "an LLM infers IdP policy," a "show the generated diff → confirm before apply" step (or a --yes flag defaulting off) between generation and the destructive delete/apply is the expected guardrail.

# Keycloak connection settings
KEYCLOAK_URL=http://keycloak.localtest.me:8080
KEYCLOAK_ADMIN_USERNAME=admin
KEYCLOAK_ADMIN_PASSWORD=admin

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 — Committing a populated aiac.env with KEYCLOAK_ADMIN_PASSWORD=admin breaks the repo's .TEMPLATE convention (cf. llm_conf.yaml.TEMPLATE using <YOUR API KEY HERE>). These are demo defaults rather than a real secret, but ship aiac.env.example/.TEMPLATE and .gitignore the real file so committed creds don't become a habit in this directory.

serviceAccountName: git-issue-agent
containers:
- name: agent
image: ghcr.io/kagenti/agent-examples/git-issue-agent:latest

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:latest is non-reproducible (pin a digest/version), and this container has no securityContext (runAsNonRoot, allowPrivilegeEscalation: false, dropped capabilities, ideally readOnlyRootFilesystem). Resource requests/limits are present, which is good. Same applies to github-tool-deployment.yaml.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New /:ToDo

Development

Successfully merging this pull request may close these issues.

4 participants