Skip to content

feat(ui): collapse inference chains, pin AI agents section - #9

Merged
GautamTalksDev merged 2 commits into
mainfrom
feat/queue-legibility
Aug 30, 2026
Merged

feat(ui): collapse inference chains, pin AI agents section#9
GautamTalksDev merged 2 commits into
mainfrom
feat/queue-legibility

Conversation

@GautamTalksDev

Copy link
Copy Markdown
Owner

Inference chains and risk breakdowns collapse to a one line conclusion with a disclosure control, so cards are scannable at a glance. Adds a dedicated AI agents section above Attributed, with unregistered agents visually emphasised and Keyring's own identity labelled as self inventory. Headline states the unregistered agent count.

Inference chains and risk breakdowns collapse to a one line conclusion with a
disclosure control, so cards are scannable at a glance. Adds a dedicated AI
agents section above Attributed, with unregistered agents visually emphasised and
Keyring's own identity labelled as self inventory. Headline states the
unregistered agent count.
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Improve approval queue legibility for AI agents

✨ Enhancement 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Group AI agents above attributed identities and emphasize unresolved registrations and Keyring
 self-inventory.
• Collapse inference chains and risk breakdowns into scannable summaries with disclosure controls.
• Add unique unregistered-agent counts to scan headlines with formatter coverage.
Diagram

graph TD
  Input["Scan cards"] --> Helpers["Format helpers"] --> Queue["Approval queue"] --> Sections["Queue sections"] --> Card["Approval cards"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Expose structured summaries from the API
  • ➕ Avoids parsing inference and risk meaning from presentation strings
  • ➕ Keeps summary semantics stable if backend wording changes
  • ➖ Requires API schema and producer changes
  • ➖ Expands scope beyond the queue UI
2. Use declaration status for registration
  • ➕ Directly reflects the principal registration field
  • ➕ Separates policy attribution from agent registration
  • ➖ May conflict with the product definition of unmatched agents
  • ➖ Could miss agents declared in source data but unresolved by policy

Recommendation: The client-side approach is appropriate for this focused UI enhancement because it reuses existing attribution semantics without an API migration. If inference wording or registration semantics will evolve independently, follow up with explicit structured API fields rather than extending string parsing.

Files changed (4) +151 / -19

Enhancement (3) +124 / -19
ApprovalCardView.tsxCondense card evidence and emphasize agent status +61/-15

Condense card evidence and emphasize agent status

• Replaces always-expanded attribution reasoning and risk lists with one-line conclusions and disclosure controls. Highlights unregistered agent cards, adds agent badges, and labels Keyring-owned identities as self-inventory.

apps/web/src/components/ApprovalCardView.tsx

ApprovalQueue.tsxAdd a dedicated AI agents queue section +45/-4

Add a dedicated AI agents queue section

• Separates AI agent cards from non-agent attribution groups and renders them above Attributed cards. Adds an agent-specific section tone and uses the unique agent identity count.

apps/web/src/components/ApprovalQueue.tsx

format.tsCount and describe unregistered agents +18/-0

Count and describe unregistered agents

• Adds a shared unresolved-agent classifier, counts unique unregistered agent identities, and includes nonzero counts in scan summary headlines.

apps/web/src/lib/format.ts

Tests (1) +27 / -0
format.test.tsCover unregistered-agent summary counts +27/-0

Cover unregistered-agent summary counts

• Updates summary expectations for the new count and verifies unmatched AI agents are uniquely counted and included in headline text.

apps/web/src/lib/format.test.ts

@qodo-code-review

qodo-code-review Bot commented Aug 30, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Declared agents marked unregistered ✓ Resolved 🐞 Bug ≡ Correctness
Description
isUnregisteredAgent equates missing attribution with registration status, so a declared AI agent
that reconciliation cannot resolve is labeled, counted, and emphasized as “Unregistered.” The domain
already carries principal.declarationStatus, and core approval logic uses that field to determine
whether an agent is undeclared in policy.
Code

apps/web/src/lib/format.ts[R93-94]

+export function isUnregisteredAgent(card: ApiCard): boolean {
+  return card.grant.principal.kind === "ai_agent" && card.attribution.resolvedTo === undefined;
Relevance

●●● Strong

Clear domain-state conflation: declaration status is authoritative, and the test exposes the
incorrect unmatched-attribution behavior.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The API model represents registration independently from attribution, and core behavior already keys
policy-registration handling on declarationStatus. The new test even constructs a declared agent
without resolvedTo and expects it to be unregistered, directly demonstrating the state conflation.

apps/web/src/api/types.ts[28-37]
packages/core/src/approval-build.ts[119-129]
apps/web/src/lib/format.test.ts[143-165]
packages/connectors/src/agent-identity/connector.ts[136-151]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Classify unregistered AI agents from `principal.declarationStatus` rather than from missing attribution. Attribution resolution and policy registration are separate states, so unresolved declared agents must not be labeled or counted as unregistered.

## Issue Context
The principal model exposes `declarationStatus: "declared" | "unregistered"`, and core approval logic already treats the `"unregistered"` value as the source of truth for agents not declared in policy. Update tests so a declared unresolved agent remains non-unregistered and add coverage for an actual `declarationStatus: "unregistered"` agent.

## Fix Focus Areas
- apps/web/src/lib/format.ts[93-95]
- apps/web/src/lib/format.test.ts[143-165]
- apps/web/src/components/ApprovalCardView.tsx[77-95]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Navigation skips visual order ✓ Resolved 🐞 Bug ≡ Correctness
Description
The new partition renders non-agent unattributed cards, then all agents, then attributed cards, but
focusable and the j/k handlers still traverse the original ordered array where every
unresolved card—including agents—comes first. With mixed agent and human cards, arrow/j/k navigation
therefore jumps between visually non-adjacent sections and actions can target a card in a different
order than the displayed queue.
Code

apps/web/src/components/ApprovalQueue.tsx[R50-53]

+  const agents = ordered.filter((card) => card.grant.principal.kind === "ai_agent");
+  const unattributed = ordered.filter(
+    (card) => card.grant.principal.kind !== "ai_agent" && isUnattributed(card),
+  );
Relevance

●●● Strong

Clear keyboard-order correctness bug; accepted UI state-transition bugs show reviewers fix
user-visible interaction inconsistencies.

PR-#3
PR-#7

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The rendered sections have a different order from focusable: rendering puts non-agent unattributed
cards first and agents second, while sortCards puts all unresolved cards first regardless of
principal kind. The keyboard handlers increment and decrement indices in that mismatched array.

apps/web/src/components/ApprovalQueue.tsx[49-67]
apps/web/src/components/ApprovalQueue.tsx[114-140]
apps/web/src/components/ApprovalQueue.tsx[307-412]
apps/web/src/lib/format.ts[163-169]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Build keyboard focus order from the same section sequence used to render the queue. The current `ordered` array no longer matches DOM order after AI-agent cards were moved into a dedicated middle section.

## Issue Context
Pending-card navigation and card `onFocus` index lookup both use `focusable`, so it should preserve `unattributed -> agents -> attributed` ordering (or rendering should use the same order as `focusable`).

## Fix Focus Areas
- apps/web/src/components/ApprovalQueue.tsx[49-67]
- apps/web/src/components/ApprovalQueue.tsx[114-140]
- apps/web/src/components/ApprovalQueue.tsx[307-412]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Agent section count mismatches ✓ Resolved 🐞 Bug ≡ Correctness
Description
The AI-agent section renders one card per grant but its heading displays the deduplicated identity
count, so multiple grants for one agent show several cards beside a smaller count. The other queue
section headings use their rendered array lengths, making this new count misleading to users
scanning the queue.
Code

apps/web/src/components/ApprovalQueue.tsx[349]

+                  count={summary.agentIdentities}
Relevance

●●● Strong

Displayed card count and heading count are deterministically inconsistent; straightforward
user-facing correctness fix.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The section maps every entry in agents, while summary.agentIdentities is calculated with a Set
keyed by agent ID or label. In contrast, the Unattributed and Attributed headings display their
corresponding card-array lengths.

apps/web/src/components/ApprovalQueue.tsx[307-316]
apps/web/src/components/ApprovalQueue.tsx[343-352]
apps/web/src/components/ApprovalQueue.tsx[379-387]
apps/web/src/lib/format.ts[26-34]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Make the AI-agent section badge represent the number of items rendered in that section, as the other section badges do. `summary.agentIdentities` deduplicates identities and therefore is not the number of agent cards.

## Issue Context
Use `agents.length` for consistency, or explicitly redesign and label all section counts if identity counts rather than card counts are intended.

## Fix Focus Areas
- apps/web/src/components/ApprovalQueue.tsx[343-352]
- apps/web/src/lib/format.ts[26-34]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: ⚖️ Balanced: Downgraded extended -> standard: change is below the extended eligibility bar (hunks 15/18, lines 170/200; both must reach the floor). Router rationale: This changes several independent UI rendering, queue partitioning, and summary/counting paths, creating a bug-dense set of subtle consistency and classification risks that benefits from redundant review passes.

Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread apps/web/src/lib/format.ts Outdated
Comment thread apps/web/src/components/ApprovalQueue.tsx Outdated
Comment thread apps/web/src/components/ApprovalQueue.tsx Outdated
Read principal.declarationStatus instead of inferring registration from
missing attribution. Drive keyboard navigation from the rendered queue
sections and count agent cards by grants shown.

Co-authored-by: Cursor <cursoragent@cursor.com>
@GautamTalksDev
GautamTalksDev merged commit 1bede2c into main Aug 30, 2026
1 check passed
@qodo-code-review qodo-code-review Bot mentioned this pull request Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant