Skip to content

Feat: AIAC Phase 1 — functional agent, UC-1 service onboarding + integration tests - #710

Merged
oblinder merged 290 commits into
rossoctl:mainfrom
s-and-p-team:aiac-phase1-pr
Aug 2, 2026
Merged

Feat: AIAC Phase 1 — functional agent, UC-1 service onboarding + integration tests#710
oblinder merged 290 commits into
rossoctl:mainfrom
s-and-p-team:aiac-phase1-pr

Conversation

@oblinder

@oblinder oblinder commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Delivers Phase 1 of the AI-based Access Control (AIAC) Kagenti extension and the UC-1 (service onboarding) use case, addressing #645.

AIAC translates a natural-language access-control policy into concrete PDP (OPA) rules for AI agents on Kubernetes. This PR lands the full Phase 1 stack plus the integration-test ladder that proves UC-1 end-to-end by evaluating the generated Rego (Phase 1 explicitly defers live enforcement/traffic).

Scope note: This PR covers the implementation, the integration-test suite, and the demo scenario workloads (github-agent + simplified github-tool) that the integration tests drive. The integration suite is not itself a demo. The runnable demo flow / walkthrough will follow in a separate PR.

What's included

Core services & library (aiac/src/aiac/, all Python 3.12, idp / pdp / policy namespace split):

  • IdP Configuration Service (aiac.idp.configuration) — Keycloak Admin REST proxy exposing subjects/roles/services/scopes with per-service enrichment; stamps the aiac.managed provisioning marker.
  • PDP Policy Writer (OPA) — filesystem-stub writer emitting {slug}.inbound.rego + {slug}.outbound.rego (the Phase 1 Interface-Pod deployment).
  • Policy Store — SQLite-backed FastAPI StatefulSet holding the authoritative ServicePolicyModel/AgentPolicyModel state; SPM-centric.
  • Policy Computation Engine (aiac.policy.computation) — in-process library; single compute_and_apply(rules) entry point, additive merge with drift-GC + authoritative decommission() offboard.
  • Policy model (aiac.policy.model) — PolicyRule, ServicePolicyModel, AgentPolicyModel, PolicyModel.

AIAC Agent (aiac.agent) — FastAPI Controller dispatching /apply/* to UC sub-agents:

  • UC-1 Service Onboarding orchestrator — classifies a service from its kagenti.io/type label, discovers capabilities (AgentCard for agents; MCP tools/list for tools), provisions roles/scopes into Keycloak, runs the Policy Rules Builder (PRB, LLM temperature=0), and calls the PCE.
  • Policy-update (build/rebuild) and role-update sub-agents; service-offboarding (UC-4) route.
  • Per-scope two-gate AND outbound gate with capability-match (RC-B); user gate discriminates.

Demo scenario workloads (aiac/demo/) — github-agent (A2A, source + issue skills) and a simplified github-tool MCP server, with their k8s manifests. These are the workloads onboarded by the integration tests.

Kubernetes manifests (aiac/k8s/) — Interface Pod, Policy Store StatefulSet, Agent deployment (Phase 1).

Specsaiac/docs/specs/ (master PRD + per-component + integration-test specs).

Testing

  • Unit suite is green: .venv/bin/pytest test/ -m "not integration" (no --ignore flags needed after the Wave 3 SPM migration).
  • Integration-test ladder (aiac/test/integration/, @pytest.mark.integration, opa-gated) — uc1-onboarding-pipeline: drives the real UC-1 agent (POST /apply/service/{id}) against really-deployed workloads and asserts the generated Rego with opa eval, computing verdicts from scenario_uc1.py (the oracle):
    • Rung 1 (agent only), Rung 2 (agent → tool), Rung 3 (tool → agent, asserts onboarding-order-independence vs rung 2). Rung 4 (two policies) deferred.
    • Rung 1 verified fully green live; RC-A (stale store) and RC-B (outbound gate) defects surfaced by the first live run are resolved on this branch (store clear-all endpoint + per-scope two-gate AND capability-match).
  • Also ships policy-pipeline and pdp-policy-writer standalone launcher integration tests.

Out of scope (Phase 1)

Live trigger path / NATS Event Broker, live enforcement & token exchange, writing Rego to the AuthorizationPolicy CR, the RAG knowledge base, and UC-2/UC-3 — all deferred to later phases per the PRD.

Notes for reviewers

  • Almost all changes are under aiac/. There are two root-level changes: a new pyrightconfig.json (tooling config), and a modification to .github/workflows/security-scans.yaml that adds a 5-line allow-ghsas exception for a chromadb/crewai transitive CVE (with a documented in-file justification). The latter is a security-policy exception and is the more reviewer-relevant of the two — please review it explicitly.
  • Specs of record: aiac/docs/specs/PRD.md and aiac/docs/specs/integration-test/uc1-onboarding-pipeline.md.

Addresses #645 (Phase 1 implementation + integration tests; runnable demo flow to follow in a separate PR).

Assisted-By: Claude (Anthropic AI) noreply@anthropic.com

oblinder and others added 30 commits June 7, 2026 20:37
Add step-by-step event and call flow diagrams for UC-1a (service
on-boarding), UC-1b (realm role on-boarding), UC-2a (incremental policy
update), and UC-2b (full rebuild) under a new Call Flows subsection in
the Technical Addendum.

Restructure the Technical Addendum: promote Key Interface Surface to a
named subsection (###) and demote the five endpoint description
subsections to ####, keeping Call Flows at the same ### level.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Incorporated Abstract, Problem Description, Problem Solution, Major
Use-Cases, Component Architecture overview, and Kagenti/Keycloak/OPA
Interfaces sections from ARCHITECTURE-SUMMARY.md into PRD.md.

Restructured PRD layout:
- Removed redundant Purpose section; promoted its design content to
  standalone Problem Description, Problem Solution, and Design
  Principles sections
- Added Major Use-Cases (UC-1 through UC-4) as a top-level section
- Added Component Summary table and data-flow diagram to Architecture
  Overview
- Added Kagenti / Keycloak / OPA Interfaces section
- Grouped component sections 7.1–7.8 under a single AIAC System
  Components parent section
- Renumbered all sections 1–10 in document order

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
… semantics

Replace generic (read config)/(set policy) labels in both the
architecture summary and PRD diagrams with descriptive italic call
text matching the PDP interface endpoints.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Technology-neutral greenfield evaluation of Red Hat AMQ Broker, AMQ
Streams, and AMQ Interconnect against the ten functional requirements
derived from the AIAC PRD. AMQ Broker satisfies all requirements;
selection guidance covers conditions where it is preferred over the
PRD's NATS JetStream choice.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…l Addendum

Move Call Flows before Short-Term Objectives for a more logical reading
order (structure before goals). Remove the Technical Addendum wrapper,
promoting Call Flows to a top-level section. Fix missing blank line before
the NATS JetStream interface divider.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Reshape models, refactor Configuration and Policy to stateful class APIs
based on design decisions from grilling session.

Models:
- Remove Assignments and Permission
- Role: drop clientRole; add childRoles (list[Role]), mappedScopes (list[Scope])
- Subject: add roles (list[Role])
- Service: drop clientId, protocol, publicClient; add type (Agent/Tool), roles, scopes
- Scope: drop protocol
- Definition order: Subject → Role → Service → Scope with model_rebuild() calls

Configuration: refactor from module-level functions to stateful Configuration
class with for_realm() factory; drop get_subject_assignments,
get_service_permissions, get_role_composites

Policy: refactor from module-level functions to stateful Policy class with
for_realm() factory; drop all composite/permission functions; rename
create_service_scope → create_scope

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
… PRD

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…ients

Reshape aiac.pdp.library to match the redesigned PRD (issues 1.6, 2.9,
2.10). Implementation done via TDD red-green-refactor cycles.

Models (issue 1.6):
- Remove Assignments, Permission; remove clientRole from Role; remove
  clientId/protocol/publicClient from Service; remove protocol from Scope
- Add Service.type: Literal["Agent","Tool"] | None
- Add nested roles/scopes lists to Subject, Role, Service
- Add childRoles/mappedScopes to Role; call model_rebuild() for forward refs

Configuration (issue 2.9):
- Rewrite module-level functions as Configuration class with for_realm()
  classmethod factory; reads AIAC_PDP_CONFIG_URL env var with fallback
- get_subjects / get_roles / get_services / get_scopes return typed lists

Policy (issue 2.10):
- Rewrite as Policy class with for_realm() factory; reads AIAC_PDP_POLICY_URL
- create_scope POSTs to /services/{id}/scopes and returns Scope

Supporting changes:
- Empty pdp/__init__.py and pdp/library/__init__.py (no re-exports at pkg root)
- Adapt read_api_from_config to new model shapes; stub out-of-scope functions
  (get_subject_assignments, get_service_permissions, get_role_composites)
  pending issues 3.5 / 3.7–3.11
- Rewrite test_models.py (31 tests), test_configuration.py (15 tests),
  test_policy.py (8 tests) — all 54 pass
- Update show_keycloak_data.py to the new class-based API; confirmed green
  against live PDP config service in kind-kagenti cluster

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…dule

Scopes are configuration entities, not policy entities. The library split
is by domain: configuration (subjects/roles/services/scopes read+write)
vs policy (composite role mappings / Rego rules). Move create_scope and
its HTTP endpoint from AIAC_PDP_POLICY_URL to AIAC_PDP_CONFIG_URL.

Update PRD.md to remove the read-only characterisation of the PDP
Configuration Service and the configuration library module.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…d}/scopes endpoint

- Add `Configuration.create_scope(service_id, scope_name, description)` to
  `aiac.pdp.library.configuration` — POSTs to the config service and returns
  a typed `Scope` instance
- Strip `Policy` to a skeleton (`__init__` + `for_realm` only); scope creation
  belongs to `Configuration`, not `Policy`
- Add `POST /services/{service_id}/scopes` to the PDP Configuration Keycloak
  service: creates a realm-level client scope and assigns it as a default scope
  to the given service in a single operation (201 on success, 502 on
  KeycloakError)
- Add `pydantic` to the configuration service `requirements.txt`
- Update PRD (`pdp-configuration-service.md`) with the new endpoint spec
- Full TDD coverage: 93 unit tests pass

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Replace the atomic `create_scope(service_id, scope_name, description)`
with two separate methods: `create_scope` (realm-level creation) and
`map_scope_to_service` (assigns existing scope to a service, returns
refreshed Service). Add matching `create_role` / `map_role_to_service`
methods for realm-level roles.

Update the PDP Configuration Service PRD accordingly: remove the old
atomic `POST /services/{id}/scopes`, add `POST /scopes`, `POST /roles`,
`POST /services/{id}/scopes/{scope_id}`, `POST /services/{id}/roles/{role_id}`,
and `GET /services/{id}` endpoints with Keycloak Admin API mappings and
409/502 response semantics.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Add 5 new write endpoints to the PDP Configuration Service (issue 1.9):
- GET /services/{service_id}
- POST /scopes (creates realm-level scope with protocol openid-connect)
- POST /services/{id}/scopes/{scope_id} (assigns scope as default)
- POST /roles (creates realm-level role)
- POST /services/{id}/roles/{role_id} (assigns role to service scope mappings)
All endpoints return 409 on conflict and 502 on KeycloakError.

Update Configuration library client (issue 2.9) to replace the old
create_scope(service_id, ...) with four new write methods:
- create_scope(scope_name, scope_description) → POST /scopes
- map_scope_to_service(service, scope) → POST + GET re-fetch
- create_role(role_name, role_description) → POST /roles
- map_role_to_service(service, role) → POST + GET re-fetch

Add unit tests for all 5 service endpoints (issue 2.11) and update
library configuration tests to cover the new write methods (issue 3.13).

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Extract Service Onboarding, Policy Update, and Role Update (formerly
Realm Roles) into dedicated sub-PRDs under components/aiac-agent/.
Trim aiac-agent.md to shared infrastructure only (NATS Consumer,
Controller, Shared Module, Validate Node, Config, Error Handling).
Add scoped architecture diagrams to each UC sub-PRD. Rename Realm
Roles orchestrator to Role Update across all PRD documents.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
aiac.apply.build → aiac.apply.policy.build
POST /apply/build → POST /apply/policy/build
POST /apply/rebuild → POST /apply/policy/rebuild

Updated across uc2-policy-update.md, aiac-agent.md, ARCHITECTURE-SUMMARY.md,
PRD.md, event-broker.md, and rag-ingest-service.md.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
All AIAC components (Agent, Event Broker, PDP Interface, RAG) now use
the domain term 'role'. Keycloak-facing services translate to the
Keycloak 'realm role' concept internally at the boundary.

- NATS subject: aiac.apply.realm-role.{id} → aiac.apply.role.{id}
- HTTP path: /apply/realm-role/{id} → /apply/role/{id}
- Python dirs: realm_roles/ → roles/, realm_role/ → role/
- Prose, Mermaid labels, section headings updated across 13 files
- python-keycloak method names and Keycloak Admin API URLs unchanged

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
…o aiac

Signed-off-by: Anatoly Koyfman <anatoly@il.ibm.com>
Closes issues 3.11, 3.12, 3.15 (implementation) and 4.10, 4.11, 4.12 (unit tests).

- shared/state.py: TriggerContext, BaseAgentState, PDPSnapshot, Permission,
  Assignments, CompositeMapping, ProposedDiff, ValidationVerdict
- shared/nodes.py: fetch_policy, fetch_domain_knowledge (ChromaDB)
- roles/role/nodes.py: fetch_pdp_state, propose_mappings, validate_mappings
  (4-check: scope, existence, safety guard, auditor LLM), apply_mappings,
  format_response
- roles/role/graph.py: RoleGraph compiled StateGraph
- roles/orchestrator.py: thin dispatcher for role/{id} trigger
- controller/nats_consumer.py: JetStream consumer with ack-after-await
  semantics and connection-loss retry
- pdp/library/policy.py: stub add/remove_role_composites (NotImplementedError)
- test/agent/: 35 unit tests; all pass with pytest -m "not integration"

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
…sues 3.2, 4.1)

Add FastAPI controller with four /apply/* routes dispatching to role,
policy, and service orchestrators. Fix fetch_domain_knowledge to raise
503 when ChromaDB is unavailable (was silently returning []). Add unit
tests for shared nodes and controller routes (20 new tests, 190 total).

- controller/routes.py: FastAPI app, lifespan with NATS consumer,
  stub handlers for policy and service orchestrators
- controller/Dockerfile + requirements.txt: python:3.12-slim base,
  build context aiac/src/
- shared/nodes.py: raise 503 on ChromaDB unavailability in both nodes

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Replace light pastel fills (#dbeafe, #fef9c3, #dcfce7, #fee2e2) with
dark equivalents and add explicit text/stroke colors so node labels
are legible in VS Code dark theme markdown preview.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
The diagram file had no inbound references from PRD.md or any
sub-component spec, making it an orphaned visual supplement.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>

Signed-off-by: Oleg Blinder <olegb@il.ibm.com>
Comment thread aiac/src/aiac/policy/store/library/api.py Fixed
Comment thread aiac/src/aiac/policy/store/library/api.py Fixed
Comment thread aiac/src/aiac/policy/store/service/main.py Fixed
Comment thread aiac/src/aiac/policy/store/service/main.py Fixed
Comment thread aiac/src/aiac/policy/store/service/main.py Fixed
oblinder added 2 commits July 29, 2026 19:41
Resolve the 11 findings from the follow-up CodeRabbit review:

- github_tool Dockerfile: install -r requirements.txt (was hardcoding
  mcp[cli]+uvicorn, dropping the pinned bounds and httpx).
- pdp-policy-writer-opa spec: correct the outbound behaviour note
  (subject_ok matches subject_role_scopes[role], target_ok matches
  target_scopes[input.target]); fix the stale file tree
  (aiac/pdp/library/policy.py -> aiac/pdp/policy/library/api.py); add a
  note reconciling the client-UUID vs clientId identifiers used at the
  onboarding vs writer layers.
- idp-configuration-service spec: reconcile the /services/{id}/roles
  summary row, behaviour note, and Assumption 3 with the code, which
  returns client roles PLUS aiac.managed realm roles on the service
  account.
- ARCHITECTURE-SUMMARY: mark the NATS Event Broker / subscription path as
  Phase 2 and the direct Controller /apply/* invocation as the Phase 1
  trigger, matching event-broker.md; align the library import path with
  the .api submodule convention.
- uc1-service-onboarding spec: make the focus-resolution snippet use a
  default + explicit 404 (matches builder.py and the surrounding prose).
- k8s: rename the standalone pod's ConfigMap to aiac-pdp-config-standalone
  to avoid clobbering the interface ConfigMap; document the deployment as
  a Phase 1 local/Kind dev topology (node-local images).

Docs/specs and build config only; no source changes, unit suite unaffected.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Address 8 open GitHub Advanced Security (CodeQL) alerts on PR rossoctl#710:

- py/stack-trace-exposure (x3, store/service/main.py): SQLite errors were
  echoed to clients via {"error": str(e)}. Log the exception server-side
  and return a generic {"error": "database error"} body instead.
- py/log-injection (engine.py): strip CR/LF from service_id before logging
  so a hostile id cannot forge log records.
- py/partial-ssrf (pdp/policy/library/api.py): URL-encode the agent_id path
  segment with quote(..., safe=''); also fixes routing for slash-bearing
  clientIds so they cannot split into extra path segments.
- py/partial-ssrf (x2, store/library via keying.py): assert the base64url
  encoded service_id matches [A-Za-z0-9_-]+ before it reaches a request URL.
- py/path-injection (rego.py slugify): assert the slug is a single
  [a-z0-9_]+ segment (blocks path traversal, rejects empty slugs) before it
  becomes a filename in the PDP Policy Writer output dir.

No behaviour change for valid inputs; full unit suite (466 tests) green.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
oblinder added a commit to s-and-p-team/cortex that referenced this pull request Jul 29, 2026
Address 8 open GitHub Advanced Security (CodeQL) alerts on PR rossoctl#710:

- py/stack-trace-exposure (x3, store/service/main.py): SQLite errors were
  echoed to clients via {"error": str(e)}. Log the exception server-side
  and return a generic {"error": "database error"} body instead.
- py/log-injection (engine.py): strip CR/LF from service_id before logging
  so a hostile id cannot forge log records.
- py/partial-ssrf (pdp/policy/library/api.py): URL-encode the agent_id path
  segment with quote(..., safe=''); also fixes routing for slash-bearing
  clientIds so they cannot split into extra path segments.
- py/partial-ssrf (x2, store/library via keying.py): assert the base64url
  encoded service_id matches [A-Za-z0-9_-]+ before it reaches a request URL.
- py/path-injection (rego.py slugify): assert the slug is a single
  [a-z0-9_]+ segment (blocks path traversal, rejects empty slugs) before it
  becomes a filename in the PDP Policy Writer output dir.

No behaviour change for valid inputs; full unit suite (466 tests) green.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
Comment thread aiac/src/aiac/pdp/policy/library/api.py Fixed
…w findings

Two automated checks on PR rossoctl#710 were failing:

CodeQL (2 new alerts):
- py/partial-ssrf (critical, pdp/policy/library/api.py): the prior quote()
  fix did not satisfy CodeQL. Add a regex fullmatch barrier on the encoded
  segment (mirroring policy/store/keying.py, which CodeQL accepts) via a new
  _agent_id_segment() helper used by apply_agent_policy + delete_agent_policy.
- py/path-injection (high, pdp/service/policy/opa/main.py): the /health
  handler took out_dir as a FastAPI route parameter, which CodeQL treats as
  request-controlled input flowing into a filesystem path check. Resolve it
  from get_output_dir() inside the handler instead (it is operator config,
  never a request). Update TestHealth to drive REGO_OUTPUT_DIR via env.

Dependency Review:
- chromadb CVE-2026-45829 / GHSA-f4j7-r4q5-qw2c (critical, transitive via
  crewai in the github_agent demo) has no patched version (affected <= 1.5.9)
  and is already documented as a mitigated/accepted risk in the demo's
  pyproject.toml. Add it to the dependency-review-action allowlist.

Full unit suite (466 tests) green.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
oblinder added a commit to s-and-p-team/cortex that referenced this pull request Jul 29, 2026
…w findings

Two automated checks on PR rossoctl#710 were failing:

CodeQL (2 new alerts):
- py/partial-ssrf (critical, pdp/policy/library/api.py): the prior quote()
  fix did not satisfy CodeQL. Add a regex fullmatch barrier on the encoded
  segment (mirroring policy/store/keying.py, which CodeQL accepts) via a new
  _agent_id_segment() helper used by apply_agent_policy + delete_agent_policy.
- py/path-injection (high, pdp/service/policy/opa/main.py): the /health
  handler took out_dir as a FastAPI route parameter, which CodeQL treats as
  request-controlled input flowing into a filesystem path check. Resolve it
  from get_output_dir() inside the handler instead (it is operator config,
  never a request). Update TestHealth to drive REGO_OUTPUT_DIR via env.

Dependency Review:
- chromadb CVE-2026-45829 / GHSA-f4j7-r4q5-qw2c (critical, transitive via
  crewai in the github_agent demo) has no patched version (affected <= 1.5.9)
  and is already documented as a mitigated/accepted risk in the demo's
  pyproject.toml. Add it to the dependency-review-action allowlist.

Full unit suite (466 tests) green.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
The dependency-review check flagged json-repair@0.25.3 (transitive via crewai
in the github_agent demo) for GHSA-xf7x-x43h-rpqh (high, unbounded-CPU DoS via
circular JSON Schema $ref). Patched in 0.60.1. Pin it in pyproject.toml
following the existing indirect-dep pattern and refresh uv.lock (also pulls
minor crewai 1.15.2->1.15.8, mcp, pydantic-settings bumps from re-resolution).

chromadb (CVE-2026-45829, no patched version) remains allowlisted via
allow-ghsas in security-scans.yaml.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
oblinder added a commit to s-and-p-team/cortex that referenced this pull request Jul 29, 2026
Address 8 open GitHub Advanced Security (CodeQL) alerts on PR rossoctl#710:

- py/stack-trace-exposure (x3, store/service/main.py): SQLite errors were
  echoed to clients via {"error": str(e)}. Log the exception server-side
  and return a generic {"error": "database error"} body instead.
- py/log-injection (engine.py): strip CR/LF from service_id before logging
  so a hostile id cannot forge log records.
- py/partial-ssrf (pdp/policy/library/api.py): URL-encode the agent_id path
  segment with quote(..., safe=''); also fixes routing for slash-bearing
  clientIds so they cannot split into extra path segments.
- py/partial-ssrf (x2, store/library via keying.py): assert the base64url
  encoded service_id matches [A-Za-z0-9_-]+ before it reaches a request URL.
- py/path-injection (rego.py slugify): assert the slug is a single
  [a-z0-9_]+ segment (blocks path traversal, rejects empty slugs) before it
  becomes a filename in the PDP Policy Writer output dir.

No behaviour change for valid inputs; full unit suite (466 tests) green.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
oblinder added a commit to s-and-p-team/cortex that referenced this pull request Jul 29, 2026
…w findings

Two automated checks on PR rossoctl#710 were failing:

CodeQL (2 new alerts):
- py/partial-ssrf (critical, pdp/policy/library/api.py): the prior quote()
  fix did not satisfy CodeQL. Add a regex fullmatch barrier on the encoded
  segment (mirroring policy/store/keying.py, which CodeQL accepts) via a new
  _agent_id_segment() helper used by apply_agent_policy + delete_agent_policy.
- py/path-injection (high, pdp/service/policy/opa/main.py): the /health
  handler took out_dir as a FastAPI route parameter, which CodeQL treats as
  request-controlled input flowing into a filesystem path check. Resolve it
  from get_output_dir() inside the handler instead (it is operator config,
  never a request). Update TestHealth to drive REGO_OUTPUT_DIR via env.

Dependency Review:
- chromadb CVE-2026-45829 / GHSA-f4j7-r4q5-qw2c (critical, transitive via
  crewai in the github_agent demo) has no patched version (affected <= 1.5.9)
  and is already documented as a mitigated/accepted risk in the demo's
  pyproject.toml. Add it to the dependency-review-action allowlist.

Full unit suite (466 tests) green.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
@oblinder oblinder self-assigned this Jul 30, 2026
@oblinder
oblinder requested a review from abigailgold July 30, 2026 07:34
@abigailgold abigailgold added the ready-for-ai-review Request automated AI code review from clawgenti label Aug 1, 2026

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

AIAC Phase 1 is a substantial, well-scoped addition — CI is all green (CodeQL, Bandit, Dependency Review, Hadolint, Trivy all pass), all 178 commits are signed off, and secrets are correctly sourced from environment variables throughout.

Two security hygiene findings noted below; no blockers.


Reviewed by clawgenti using github:pr-review

Comment thread aiac/src/aiac/agent/controller/Dockerfile
deny-licenses: GPL-3.0, AGPL-3.0
comment-summary-in-pr: never
# CVE-2026-45829 / GHSA-f4j7-r4q5-qw2c (chromadb, transitive via crewai): no patched
# version exists (affected <= 1.5.9, crewai pins chromadb < 1.2). The vulnerability

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nit: allow-ghsas: GHSA-f4j7-r4q5-qw2c is scoped at the job level, so it silences the advisory for all packages in the scan, not just chromadb. The justification in the comment is sound (no fixed version, trust_remote_code not used), but worth noting if a future dep happens to share the advisory ID. Consider adding a follow-up ticket to re-evaluate once chromadb ships a patched release.

Add a non-root user (UID 10001) to the five service Dockerfiles under
aiac/src/aiac, matching the authbridge/sparc-service pattern:
  - agent/controller
  - idp/service/configuration/keycloak
  - pdp/service/policy/keycloak
  - pdp/service/policy/opa
  - policy/store/service (chown /data for the default-path SQLite start)

The two services that write to mounted volumes need fsGroup so the
non-root UID can write (a mount masks the image's chown, leaving the
volume root-owned). Add pod-level runAsUser/runAsGroup/fsGroup 10001 to:
  - policy-store-statefulset.yaml (PVC at /data)
  - pdp-interface-deployment.yaml (emptyDir at /rego)

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
@oblinder

oblinder commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the first finding (containers running as root) in c173113.

Added a non-root user (UID 10001) to the five service Dockerfiles under aiac/src/aiac — agent/controller, idp keycloak, pdp keycloak, pdp opa, and policy store — matching the authbridge/sparc-service pattern.

Two of those services write to mounted volumes, where the image's chown is masked by the mount and the volume stays root-owned. So I also added pod-level runAsUser/runAsGroup/fsGroup: 10001 to the manifests that back them:

  • policy-store-statefulset.yaml — PVC at /data
  • pdp-interface-deployment.yaml — emptyDir at /rego

The demo github_tool Dockerfile is intentionally out of scope here. The second finding (allow-ghsas job-level scope note) is left as-is.

Assisted-By: Claude (Anthropic AI) noreply@anthropic.com

@abigailgold

Copy link
Copy Markdown

Some additional comments/security issues/suggested cleanups from Claude.

Any non-must issues can be deferred to next PR but should be documented properly to be covered later.

General cleanup/inconsistencies:

# Severity Location Issue
1 must-fix aiac/docs/specs/components/library-policy-store.md (end of file) Committed spec text ends with leaked AI tool-call XML fragments: </parameter> / </invoke>. This is an artifact of an AI-assisted editing session that was never cleaned up before commit.
2 must-fix aiac/docs/specs/components/policy-computation-engine.md (end of file) Same class of defect: file ends with leaked </content> / </invoke> XML fragments in the committed markdown.
3 must-fix PR description ("Notes for reviewers") "Almost all changes are under aiac/; the only root change is a new pyrightconfig.json" is inaccurate as stated. .github/workflows/security-scans.yaml (repo root) was also modified — it adds a 5-line allow-ghsas exception for a chromadb/crewai transitive CVE (with a documented justification) — and this is arguably the more reviewer-relevant root change of the two, since it's a security-policy exception rather than a tooling config file. Worth calling out explicitly in the PR body.
4 suggestion PR description ("What's included") vs. actual diff The PR body explicitly claims to include "Specs & issue tracking — ... aiac/docs/issues/ (local markdown issue tracker + handoffs)." This directory does not exist anywhere in the PR's diff or head branchaiac/docs/ contains only analysis/ and specs/. Several commit messages reference "closing" specific numbered issues (1.6, 2.9, 2.10, 3.11, 3.12, 3.15, 4.1, 4.10–4.12, 6.2, etc.) that are supposed to live in this tracker, but the tracker itself never shipped, so those statuses are unverifiable from this PR alone. This looks like a stale claim left over from an earlier draft of the PR description rather than a deliberate omission, but it should be corrected or the missing directory should be added.
5 suggestion aiac/docs/specs/ARCHITECTURE-SUMMARY.md vs. aiac/docs/specs/PRD.md ARCHITECTURE-SUMMARY.md duplicates the PRD's Abstract, Problem Description, and Problem Solution sections almost verbatim. The commit history shows both had to be edited in parallel through the same terminology-rename passes; keeping two documents in sync for the same content is a standing maintenance cost with no clear reason not to consolidate into one.
6 nit aiac/docs/analysis/ (5 files, ~1,315 lines: a 789-line markdown writeup, a discovery script, an agent-card JSON, and an MCP-tools JSON+HTML dump) Reads as scratch exploration/research output rather than spec-of-record documentation. Not harmful, but it blurs the line between deliberate, maintained specs and working notes — consider a README noting its status, or moving it out of the tracked docs/ tree if it's not meant to be kept current.
7 nit aiac/k8s/aiac-deployment-guide.md (~line 1964) Tells the reader to "remove the stringData block" from pdp-interface-deployment.yaml, but that manifest contains no Secret/stringData resource — stale instruction from an earlier draft. Confusing but not a real secret leak.
8 nit aiac/demo/agents/github_agent/README.md References .env.ollama / .env.openai / .env.claude preset files that aren't part of this PR (only .env.template is added) — likely left over from drafting; will confuse anyone following the README literally.

Container security:

# Severity Location Issue
9 must-fix aiac/demo/tools/github_tool/Dockerfile No USER directive — container runs as root. Its sibling aiac/demo/agents/github_agent/Dockerfile correctly sets USER 1001, making this omission stand out as inconsistent rather than deliberate.
10 must-fix All 5 Dockerfiles under aiac/src/aiac/** (agent/controller, idp-keycloak, pdp-keycloak, pdp-opa, policy-store) None set a non-root USER, and all use unpinned FROM python:3.12-slim (mutable tag, not a digest pin).
11 must-fix aiac/demo/tools/github_tool/k8s/github-tool-deployment.yaml No resources block at all (no CPU/memory requests or limits) — unbounded resource consumption risk.
12 suggestion All manifests under aiac/k8s/ and both demo deployments No securityContext (runAsNonRoot, readOnlyRootFilesystem, etc.) anywhere.
13 suggestion aiac/k8s/agent-deployment.yaml, pdp-interface-deployment.yaml (both containers), policy-store-statefulset.yaml Missing CPU/memory requests/limits (only PVC storage is set on the StatefulSet); only github-agent-deployment.yaml has real resource limits.
14 suggestion All aiac/k8s/* and demo manifests No liveness probes anywhere (readiness probes exist on the 4 core aiac/k8s/ workloads only; the demo deployments have no probes at all).
15 nit Image tags Demo manifests use localhost/github-agent:latest / :latest while aiac/k8s/*.yaml uses :local — inconsistent convention, and only the aiac/k8s/ manifests carry a "dev topology, not production" disclaimer; the demo manifests don't.

Efficiency/design comments:

# Severity Location Issue
16 suggestion aiac/src/aiac/policy/computation/engine.py::_run changed.add(model.service_id) fires on every override=True call even when the incoming rule was already present (no dedup on no-op), causing an extra apply_service_policy store write per rule during rebuild/role_update flows. Not a correctness bug, just wasted I/O.
17 nit aiac/src/aiac/idp/service/configuration/keycloak/main.py::_get_or_create_admin Keycloak admin credentials/session are cached for the process lifetime with no TTL or rotation path — a credential rotation requires a service restart. Worth a docstring note if this is intentional.
18 nit aiac/src/aiac/policy/store/service/main.py (~two call sites) Two read paths access _cache without the _write_lock that a nearby comment implies protects consistency. Safe today under CPython's dict-read atomicity, but could mislead a future contributor into assuming the lock covers reads too.
19 nit pdp/service/policy/keycloak/Dockerfile and pdp/service/policy/opa/Dockerfile Both EXPOSE 7072 — harmless since they're separate Deployments, but worth a sanity check against the port-allocation documentation given how many port remaps happened in the commit history.

Record the AIAC service deployment convention in aiac/CLAUDE.md: all
service images run as non-root UID 10001, and services that write to a
mounted volume (policy store /data PVC, OPA writer /rego emptyDir) also
need pod-level securityContext fsGroup so the kubelet chowns the volume.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
@oblinder

oblinder commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

DCO status

The sign-off mismatch is fixed. 218 commits were authored/committed as Oleg Blinder <olegb@il.ibm.com> but signed off as <oblinder@gmail.com>, so DCO rejected them. I rewrote those commits' author/committer email to oblinder@gmail.com to match the sign-off (name unchanged, file trees byte-identical — metadata-only). Every commit in the PR now has author = committer = Signed-off-by:

  • 245 × Oleg Blinder <oblinder@gmail.com>
  • 43 × Anatoly Koyfman <anatoly@il.ibm.com>

0 sign-off mismatches remain. This required a force-push of aiac-phase1-pr.

Remaining DCO failure is an app limitation, not a real violation. The DCO check now reports:

The DCO check could not be evaluated because the complete pull request commit list could not be retrieved. GitHub returned 250 commits from the REST API, so the app attempted to retrieve the full list through GraphQL before evaluating the pull request. That fallback did not complete… Please retry by re-running the check or pushing a new commit.

The PR has 288 commits (> the 250 REST cap), and the app's GraphQL fallback isn't completing. Pushing new commits only raises the count; the API rerequest endpoint returns 404 for this app.

Ask for a maintainer: please either Re-run the DCO check from the Checks tab (the UI uses different auth and the fallback failure appears transient), or admin-merge / mark DCO non-required for this PR — the sign-offs themselves are now fully compliant.

Assisted-By: Claude (Anthropic AI) noreply@anthropic.com

@abigailgold

Copy link
Copy Markdown

Documenting my understanding of the scope of this PR and suggestions for future releases:

  • Onboarding use case includes both agents and tools (toolkits), and covers also updates to agents and tools, not just addition of new ones.
  • An agent can have more than 1 role, currently all possible roles are considered, there is no attempt to distinguish between them at runtime.
  • Roles and scopes are currently agent specific and not shared - shared roles and scopes planned for future releases.
  • There can be duplication between inbound and outbound rules of different agents - not currently handled.
  • Policy store saves only current policy model. For future versions, suggest to support policy history and versioning (for auditing purposes).
  • In phase 1 the automatic trigger for onboarding is not implemented. It is done on demand using a REST API. Future - using an event broker (NATS).
  • Integration tests rely on demo containers being deployed separately. This is documented in the integration tests spec. In the future suggest to have this performed automatically, so that installation of AIAC component is the only prereq to tests running.

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

Once all must-fix comments are addressed and whatever is deferred to a future PR is well documented, I approve this PR.

Resolves the must-fix severity items from abigailgold's review:

- Remove leaked AI tool-call XML fragments (</parameter>, </invoke>,
  </content>) accidentally committed at the end of three specs:
  library-policy-store.md, policy-computation-engine.md, and
  policy-store.md (the last not flagged but same defect class).
- github_tool Dockerfile: add non-root user (USER 10001); it was
  running as root, inconsistent with its sibling github_agent.
- Digest-pin FROM python:3.12-slim in the github_tool Dockerfile and
  the five src/aiac service Dockerfiles, reusing the digest already
  vetted in authbridge/demos/hr-cpex. (Non-root USER on the five
  service images was added in a prior commit.)
- github-tool-deployment.yaml: add CPU/memory requests and limits.

The PR description was also corrected to disclose the
security-scans.yaml allow-ghsas change as a second root-level change.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
@oblinder

oblinder commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Addressed all must-fix severity findings from the two tables above in d83f0db (pushed to aiac-phase1-pr).

General cleanup/inconsistencies

  • 1 (must-fix)library-policy-store.md: removed the leaked AI tool-call XML fragments (</parameter> / </invoke>) at end of file.
  • 2 (must-fix)policy-computation-engine.md: removed the leaked </content> / </invoke> fragments at end of file.
  • 3 (must-fix) — PR description ("Notes for reviewers"): corrected the "only root change is pyrightconfig.json" claim. It now explicitly calls out the second root-level change — the allow-ghsas exception in .github/workflows/security-scans.yaml — and flags it as the more reviewer-relevant one (a security-policy exception).

While in there I also cleaned a third committed spec with the same leaked-XML defect that wasn't flagged: policy-store.md. The identical fragments also live under aiac/docs/issues/, but that directory is gitignored and not part of this PR (which also confirms finding 4 — the issue tracker never shipped in the diff), so those were left untouched.

Container security

  • 9 (must-fix)demo/tools/github_tool/Dockerfile: added a non-root user (USER 10001); it was running as root, inconsistent with its sibling github_agent.
  • 10 (must-fix) — the five src/aiac/** Dockerfiles: digest-pinned FROM python:3.12-slim, reusing the digest already vetted in authbridge/demos/hr-cpex/agent/Dockerfile. (The non-root USER on these five was already added earlier in c173113; this commit closes the remaining unpinned-base-image half of the finding, and applies the same pin to the github_tool image.)
  • 11 (must-fix)demo/tools/github_tool/k8s/github-tool-deployment.yaml: added CPU/memory requests and limits.

Deferred (non-must-fix)

The suggestions/nits (4–8, 12–19) are intentionally out of scope for this commit and are not yet addressed. Per the review guidance, these should be tracked for a follow-up PR.

Assisted-By: Claude (Anthropic AI) noreply@anthropic.com

Resolves the suggestion-tier items from the PR rossoctl#710 review:

- rossoctl#5 Docs consolidation: remove ARCHITECTURE-SUMMARY.md, which duplicated
  the PRD's Abstract/Problem/Solution/Use-Cases/diagram almost verbatim and
  carried an older (pre-compute_and_apply) call-flow. The PRD is the single
  spec of record.
- rossoctl#12 Pod-security hardening across all k8s/ and demo manifests: pod-level
  runAsNonRoot + seccompProfile RuntimeDefault, container-level
  allowPrivilegeEscalation:false, readOnlyRootFilesystem:true, and
  capabilities drop ALL. Writable /tmp emptyDir added where the root FS is
  read-only. The demo github-agent hardens at the container level only (its
  injected AuthBridge sidecar runs as UID 1337) and omits readOnlyRootFilesystem
  (uv/litellm/crewai write caches under HOME=/app).
- rossoctl#13 CPU/memory requests + limits on the agent, both pdp-interface
  containers, the policy-store, and the standalone idp pod.
- rossoctl#14 Liveness probes on every workload (httpGet /health where exposed,
  tcpSocket for the Controller and demo workloads that expose none).

Also updates aiac/CLAUDE.md to document the new pod-security hardening
baseline (the prior note that volume-less services need no manifest
securityContext is now stale).

The PR description wording (rossoctl#4, the stale docs/issues/ tracker claim) was
corrected directly on the PR.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Oleg Blinder <oblinder@gmail.com>
@oblinder

oblinder commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the suggestion-severity items from the review in 585b7a6, plus the PR-description wording fix. Summary of resolved vs. deferred:

Resolved

  • 4 (PR description) — Removed the stale aiac/docs/issues/ tracker claim from the "What's included" section (that directory is a local-only, untracked tree and was never part of the diff). Corrected directly in the PR body.
  • 5 (docs consolidation) — Removed ARCHITECTURE-SUMMARY.md. It duplicated the PRD's Abstract / Problem Description / Problem Solution / Use-Cases / architecture diagram almost verbatim, and its call flows were older than the PRD's (they still showed the pre-compute_and_apply direct-to-PDP path). The PRD is now the single spec of record — no content was lost.
  • 12 (securityContext) — Hardened every manifest under aiac/k8s/ and both demo deployments: pod-level runAsNonRoot + seccompProfile: RuntimeDefault, and container-level allowPrivilegeEscalation: false, readOnlyRootFilesystem: true, capabilities.drop: [ALL]. A writable /tmp emptyDir is added wherever the root FS is read-only.
  • 13 (resources) — Added CPU/memory requests + limits to the agent, both pdp-interface containers, the policy-store, and the standalone idp pod.
  • 14 (liveness probes) — Added liveness probes to every workload (httpGet /health where the service exposes one; tcpSocket for the Controller and demo workloads that don't).

Two deliberate, in-file-documented deviations on the demo github-agent: hardening is set at the container level only (the operator-injected AuthBridge sidecar runs as UID 1337, so a pod-level runAsUser would clobber it), and readOnlyRootFilesystem is omitted there because its runtime (uv / litellm / crewai) writes caches under HOME=/app. The aiac/CLAUDE.md pod-security section was updated to document the new hardening baseline.

Deferred (not in this pass)

The remaining nit-tier items — 6 (docs/analysis/ status), 7 (stale stringData instruction in the deployment guide), 8 (.env.* preset references in the demo README), 15 (:latest vs :local tag convention) — and the efficiency/design comments 16–19 are not addressed here and can be tracked for a follow-up PR.

Assisted-By: Claude (Anthropic AI) noreply@anthropic.com

@oblinder

oblinder commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up on the deferred efficiency/design item — finding 16.

Reviewed it: not a real performance issue — the store writes are already bounded to one per affected service, so I'm marking it won't-fix rather than deferring. No code change needed.

Assisted-By: Claude (Anthropic AI) noreply@anthropic.com

@oblinder
oblinder merged commit 87412b2 into rossoctl:main Aug 2, 2026
18 of 19 checks passed
@github-project-automation github-project-automation Bot moved this from New/ToDo to Done in Rossoctl Issue Prioritization Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-ai-review Request automated AI code review from clawgenti

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

6 participants