Skip to content

feat(execution-history): filter approvals by type - #58

Merged
aryasaatvik merged 1 commit into
devfrom
fix/execution-history-approval-filters
Jun 24, 2026
Merged

aryasaatvik merged 1 commit into
devfrom
fix/execution-history-approval-filters

Conversation

@aryasaatvik

Copy link
Copy Markdown
Owner

Summary

Updates execution history to present approval requests as approvals instead of interactions and adds approval-type filtering for form and URL approvals.

Changes

  • Adds denormalized form and URL approval flags to run rows so the list can filter by approval type without fetching detail blobs.
  • Exposes approvalType=form|url through the list API and React filter state.
  • Updates the filter rail, command palette, table column, and detail drawer copy to use approval language.
  • Covers schema tolerance, handler query mapping, and the store approval-kind indexing path.

Tests

  • bun run --cwd packages/plugins/execution-history typecheck
  • bun run --cwd packages/plugins/execution-history test
  • bunx oxfmt --check on touched execution-history files
  • Deployed and manually checked https://executor.arya.sh/plugins/executionHistory

Known tradeoffs

Existing historical approval rows only have the old hadInteraction boolean, so their form and URL approval flags remain false unless we run a backfill from detail blobs.

Record denormalized approval kind flags on run rows so the runs list can filter form and URL approval runs without fetching detail blobs.

Update the visible execution-history UI copy from interactions to approvals and cover the handler, schema, and store paths.
@aryasaatvik
aryasaatvik marked this pull request as ready for review June 24, 2026 06:14
@greptile-apps

greptile-apps Bot commented Jun 24, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds denormalized hadFormApproval / hadUrlApproval boolean flags to RunRow, indexes them for efficient filtering, and wires a new approvalType=form|url filter through the list API, store query builder, filter rail, and command palette. UI copy for the "interaction" column is updated to "approval" throughout.

  • Schema & store: hadFormApproval/hadUrlApproval follow the same optional-with-decoding-default pattern used for actorId, keeping legacy rows decodeable. onInteractionStarted sets the flags eagerly; onExecutionFinished carries them through the buffer-lost restart path correctly.
  • Facet counts: computeMeta adds two independent aggregate-count queries for form and URL approvals, and the buildRunsWhere omit logic correctly scopes each facet count to the other active filters.
  • mergeTokens mutual exclusivity gap: The filter rail toggle handlers explicitly clear approvalType when interaction is set (and vice versa), but mergeTokens in filter-command.tsx applies both tokens independently — a user typing interaction:false approval:form in the command palette ends up with both filters active simultaneously, always yielding 0 results.

Confidence Score: 4/5

Safe to merge; the command palette can produce a contradictory filter state but only in a narrow edge case, with no data corruption risk.

The storage layer, indexing, event handling, and API are all correct and well-tested. The one gap is that mergeTokens in the command palette doesn't mirror the mutual-exclusivity logic the filter rail enforces — typing both interaction:false and approval:form together silently produces an always-empty list. This is a real but narrowly scoped UX defect that affects only direct command-palette usage with that specific combination, and it carries no risk of bad writes or data loss.

packages/plugins/execution-history/src/react/filter-command.tsx — mergeTokens needs the same mutual-exclusivity guard already present in the filter rail toggles.

Important Files Changed

Filename Overview
packages/plugins/execution-history/src/react/filter-command.tsx mergeTokens applies interaction and approvalType independently without enforcing mutual exclusivity, allowing a contradictory filter state when both are typed in the command palette simultaneously.
packages/plugins/execution-history/src/sdk/store.ts Core store correctly adds hadFormApproval/hadUrlApproval to RunBuffer and RunRow, updates buildRunsWhere with approvalType omit support, and computes approval facet counts in computeMeta. Restart-path fallback for the new flags is correct.
packages/plugins/execution-history/src/sdk/collections.ts Adds hadFormApproval/hadUrlApproval as optional boolean fields with withDecodingDefaultType(false), matching the pattern used for actorId/actorLabel. Schema tolerance for legacy rows is correct.
packages/plugins/execution-history/src/react/filter-rail.tsx Filter rail correctly clears approvalType when interaction is toggled (and vice versa). New Form/URL approval facet rows are wired up correctly.
packages/plugins/execution-history/src/api/group.ts Adds approvalType to ListRunsQuery with Schema.Literals validation, and extends RunInteractionCounts with formApproval/urlApproval fields.
packages/plugins/execution-history/src/api/handlers.ts Handler correctly passes query.approvalType through to ExecutionHistoryListOptions; schema validation ensures the value is form or url before reaching the handler.
packages/plugins/execution-history/src/react/run-row.tsx Approval badge correctly derives label from hadFormApproval/hadUrlApproval flags, with a fallback to yes for legacy rows where only hadInteraction=true is set.
packages/plugins/execution-history/src/sdk/store.test.ts New test covers the full event sequence for a form approval run and asserts correct flag values, list filtering by approvalType=form, and correct interactionCounts facet values.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[InteractionStarted event] --> B{request type?}
    B -- UrlElicitation --> C[isUrlApproval = true]
    B -- FormElicitation --> D[isFormApproval = true]
    C --> E[buffer.hadUrlApproval = true]
    D --> F[buffer.hadFormApproval = true]
    E & F --> G[putRun: waiting_for_interaction\nhadFormApproval / hadUrlApproval written]
    H[ExecutionFinished event] --> I{buffer present?}
    I -- yes --> J[use buffer flags directly]
    I -- no / restart --> K[fetch existing row from storage]
    K --> L[use existing.data.hadFormApproval ?? interactionEntries.some]
    J & L --> M[putRun: terminal row with correct flags]
    N[list API: approvalType=form] --> O[buildRunsWhere: hadFormApproval eq true]
    O --> P[queryKeyset filtered results]
    O --> Q[computeMeta: formApprovalCount, urlApprovalCount]
    R[filter-rail toggle] --> S[clears interaction when approvalType set and vice versa]
    T[command palette mergeTokens] --> U[does NOT clear interaction when approvalType set]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[InteractionStarted event] --> B{request type?}
    B -- UrlElicitation --> C[isUrlApproval = true]
    B -- FormElicitation --> D[isFormApproval = true]
    C --> E[buffer.hadUrlApproval = true]
    D --> F[buffer.hadFormApproval = true]
    E & F --> G[putRun: waiting_for_interaction\nhadFormApproval / hadUrlApproval written]
    H[ExecutionFinished event] --> I{buffer present?}
    I -- yes --> J[use buffer flags directly]
    I -- no / restart --> K[fetch existing row from storage]
    K --> L[use existing.data.hadFormApproval ?? interactionEntries.some]
    J & L --> M[putRun: terminal row with correct flags]
    N[list API: approvalType=form] --> O[buildRunsWhere: hadFormApproval eq true]
    O --> P[queryKeyset filtered results]
    O --> Q[computeMeta: formApprovalCount, urlApprovalCount]
    R[filter-rail toggle] --> S[clears interaction when approvalType set and vice versa]
    T[command palette mergeTokens] --> U[does NOT clear interaction when approvalType set]
Loading

Fix All in Codex Fix All in Cursor Cloud Agents Fix All in Claude Code Fix All in Cursor

Reviews (1): Last reviewed commit: "feat(execution-history): filter approval..." | Re-trigger Greptile

Comment on lines 70 to +71
interaction: tokens.interaction !== null ? tokens.interaction : filters.interaction,
approvalType: tokens.approvalType !== null ? tokens.approvalType : filters.approvalType,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 When mergeTokens applies a parsed token for interaction, it should clear approvalType (and vice versa), mirroring the mutual-exclusivity logic in the filter rail toggles. Without this, typing interaction:false approval:form in the command palette sets both simultaneously, producing hadInteraction=false AND hadFormApproval=true — a contradiction that always returns 0 results with no error.

Suggested change
interaction: tokens.interaction !== null ? tokens.interaction : filters.interaction,
approvalType: tokens.approvalType !== null ? tokens.approvalType : filters.approvalType,
interaction: tokens.interaction !== null ? tokens.interaction : tokens.approvalType !== null ? null : filters.interaction,
approvalType: tokens.approvalType !== null ? tokens.approvalType : tokens.interaction !== null ? null : filters.approvalType,

Fix in Codex Fix in Cursor Cloud Agents Fix in Claude Code Fix in Cursor

@aryasaatvik
aryasaatvik merged commit 8d6675a into dev Jun 24, 2026
8 checks passed
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