Skip to content

feat(web-ui): auto-run gates on execution completion, show summary in banner (#472)#494

Merged
frankbria merged 2 commits intomainfrom
feat/issue-472-gate-results-completion-banner
Mar 24, 2026
Merged

feat(web-ui): auto-run gates on execution completion, show summary in banner (#472)#494
frankbria merged 2 commits intomainfrom
feat/issue-472-gate-results-completion-banner

Conversation

@frankbria
Copy link
Owner

@frankbria frankbria commented Mar 24, 2026

Summary

  • Gates trigger automatically (non-blocking) via useEffect when execution status becomes completed
  • useRef guard ensures gates run exactly once per completion — not on re-renders
  • GateSummary sub-component renders below action buttons in the completed state banner:
    • Running: "Running quality gates…"
    • All passed: "✓ All N gates passed" (green)
    • Some failed: "⚠ N/M gates passed · View full report →" (amber, links to /review)
    • Error: "Gate check unavailable · View in Review →"
  • CompletionBannerProps extended with gateResult?, gateRunning?, gateError?

Closes #472

Test plan

  • Execution completion triggers gate run automatically (check network tab)
  • "Running quality gates…" appears immediately in banner
  • All-passed case shows green checkmark text
  • Failed-gates case shows amber warning with "View full report →" link
  • Gate errors show graceful fallback message
  • Banner appears immediately (not blocked waiting for gates)
  • Build passes (npm run build clean)

Summary by CodeRabbit

  • New Features
    • Automatic gate execution runs once when a task completes.
    • Completion banner shows gate status: pending indicator, running state, errors, and results (passed vs total).
    • Gate summary added to the completion view with direct links to review checks when issues are present.

… banner (#472)

- Gates trigger automatically (non-blocking) when execution status is 'completed'
- useRef guard prevents duplicate runs across re-renders
- CompletionBanner completed state shows gate summary below action buttons:
  - Running: "Running quality gates..."
  - All passed: "✓ All N gates passed"
  - Some failed: "⚠ N/M gates passed · View full report →" (links to /review)
  - Error: "Gate check unavailable · View in Review →"
- Extend CompletionBannerProps with gateResult, gateRunning, gateError fields
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 24, 2026

Walkthrough

Automatically run gate checks once a task execution completes and surface a preliminary gate summary (running state, pass/fail counts, or error) in the CompletionBanner via a new GateSummary component and added gate-related props.

Changes

Cohort / File(s) Summary
Gate execution & UI
web-ui/src/app/execution/[taskId]/page.tsx
Run gatesApi.run(workspacePath) once when monitor.completionStatus === 'completed' (guarded by hasRunGatesRef); store gateResult, gateRunning, gateError state; derive showGatePending; pass gate props into CompletionBanner; add GateSummary component and render it in the completed banner layout.
Types
web-ui/src/types/index.ts
Extended CompletionBannerProps with optional `gateResult?: GateResult

Sequence Diagram(s)

sequenceDiagram
    participant Page as ExecutionPage
    participant Monitor as TaskMonitor
    participant API as GatesAPI
    participant Banner as CompletionBanner
    participant Summary as GateSummary

    Monitor->>Page: completionStatus = "completed"
    Page->>Page: if not hasRunGatesRef -> set hasRunGatesRef
    Page->>API: gatesApi.run(workspacePath)
    activate API
    Page->>Page: set gateRunning = true
    API-->>Page: return gateResult / error
    deactivate API
    Page->>Page: set gateResult, gateRunning = false, gateError?
    Page->>Banner: render with gateResult, gateRunning, gateError
    Banner->>Summary: render summary (running / passed counts / link to /review)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I hopped along the execution trail,
Gates ran in secret without fail,
A banner whispers pass and fail,
Links to fixes on the review trail,
thumps a happy foot 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 PR title accurately summarizes the main change: auto-running gates on execution completion and displaying a summary in the completion banner.
Linked Issues check ✅ Passed The PR implements all core coding requirements from issue #472: auto-triggering gates on completion, displaying gate summary in CompletionBanner with pass/fail states, providing links to /review, and ensuring non-blocking execution.
Out of Scope Changes check ✅ Passed All changes directly support issue #472 requirements. The modifications to page.tsx and types/index.ts are scoped to auto-running gates and displaying results in the completion banner, with no unrelated alterations.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-472-gate-results-completion-banner

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude
Copy link

claude bot commented Mar 24, 2026

Code Review — PR #494: Auto-run gates on execution completion

Overall this is a well-scoped, clean implementation. The useRef guard is the right pattern here and the non-blocking design is correct. A few issues worth addressing before merge:


Bug: Wrong color class on gate error state

In GateSummary, the error paragraph uses text-green-700 dark:text-green-300 — the same green as the running/success states — instead of something neutral or amber:

// line 199 — should NOT be green-700 for an error state
<p className="mt-2 text-xs text-green-700 dark:text-green-300">
  Gate check unavailable · ...

It should be text-amber-700 dark:text-amber-300 (or gray) to visually distinguish a failure to run gates from a successful gate run.


Logic inconsistency: gateResult.passed vs. manual PASSED count

GateSummary uses gateResult.passed (the server-authoritative value) to decide which branch to render, but then manually counts c.status === 'PASSED' to produce the fraction shown to the user. This is fine as long as gateResult.passed === (passed === total), but GateCheck.status can also be 'SKIPPED'. A skipped gate could cause passed < total while gateResult.passed is true, which would render the failed/amber branch even though the server considers everything passing.

Consider using gateResult.summary (which the server already produces) or ensuring the count only excludes ERROR/FAILED gates, not SKIPPED ones. For example: const failed = gateResult.checks.filter(c => c.status === 'FAILED' || c.status === 'ERROR').length.


Stale hasRunGatesRef on navigation and re-mount

hasRunGatesRef is initialized as false per component mount, so a page refresh correctly re-runs gates. However, if the user navigates away and back to the same execution page (Next.js soft navigation), the component remounts and gates will re-run. That is probably acceptable behavior, but it's worth making sure gatesApi.run() is idempotent and won't cause side effects when called multiple times against a completed workspace. This appears fine based on the current gatesApi implementation (it's a pure read-trigger, no state mutation beyond the result).


Minor: GateResult import is unused in the type position

GateResult is imported at line 14:

import type { Task, CompletionBannerProps, GateResult } from '@/types';

But GateResult is only used as the type argument to useState<GateResult | null>. This is fine and correct — just noting it's not dead code in case a linter flags it.


Consistency with /review page gate-running pattern

The /review page (app/review/page.tsx) uses a try/catch with setFeedback to surface gate errors to the user. This PR uses a simpler boolean gateError flag with a static fallback message. The simpler approach is acceptable in this context (it's a secondary banner, not a primary action), but be aware the error message "Gate check unavailable" gives the user no actionable detail. If the API returns a useful error (e.g., workspace not found, server unreachable), it's silently swallowed by the .catch(() => setGateError(true)). Consider at minimum logging console.error in the catch, matching the pattern in tasksApi.getOne:

.catch((err) => {
  console.error('Gate auto-run failed:', err);
  setGateError(true);
})

CLAUDE.md alignment note

CLAUDE.md classifies the web-ui as legacy/reference: "Do not expand frontend scope during Golden Path work." This PR is purely UI — it doesn't touch core/, cli/, or any server router. This is fine as an incremental improvement to the existing web-ui, but be aware it won't be prioritized alongside Phase 4 (Agent Adapter Architecture) or Phase 5 (PROOF9) CLI work.


Summary: The core implementation is correct — useRef guard, non-blocking .then/.catch/.finally chain, and the layout restructuring are all solid. Fix the green color on the error state (clear bug), and consider the SKIPPED gate edge case. Everything else is minor polish.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web-ui/src/app/execution/`[taskId]/page.tsx:
- Around line 197-203: The gateError branch currently renders the unavailable
state using green "success" styling; update the JSX returned when gateError is
truthy (the conditional handling around gateError in the page component) to use
warning or destructive classes instead of "text-green-700 dark:text-green-300"
(for example switch to a yellow/orange or red text class) so the
unavailable/error state uses non-success styling and visually indicates a
problem; keep the message and Link component unchanged.
- Around line 54-62: The effect that runs gates waits for a state update so the
first render after monitor.completionStatus becomes 'completed' shows a blank
GateSummary; change GateSummary to compute the "pending" state from
monitor.completionStatus plus existing gateResult/gateError (i.e., treat pending
= monitor.completionStatus === 'completed' && !gateResult && !gateError) instead
of relying solely on gateRunning, and keep the useEffect as the trigger that
sets hasRunGatesRef and calls gatesApi.run(workspacePath) to populate
setGateResult/setGateError; update any conditional rendering in GateSummary to
use this derived pending condition so the UI shows the pending state immediately
on first completed render.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1f0e2b23-620c-4361-ba35-11547a687872

📥 Commits

Reviewing files that changed from the base of the PR and between 6dd9aa8 and 55860ae.

📒 Files selected for processing (2)
  • web-ui/src/app/execution/[taskId]/page.tsx
  • web-ui/src/types/index.ts

@claude
Copy link

claude bot commented Mar 24, 2026

Code Review

The overall approach is sound — useRef once-guard, non-blocking promise chain, and derived showGatePending to handle the first render race are all good React patterns. A few correctness issues need attention before merge.


Critical (confirmed bugs)

1. Wrong color on "gate running" state (page.tsx ~line 196)

The gateRunning branch uses text-green-700 dark:text-green-300. Green conventionally signals success, so this is misleading — "Running quality gates…" and "All gates passed" will look identical to users. Should use a neutral color like text-gray-600 dark:text-gray-400 to distinguish the in-progress state.

2. SKIPPED gates cause false-negative fraction display (page.tsx ~line 211)

const passed = gateResult.checks.filter((c) => c.status === 'PASSED').length;

GateStatus includes 'SKIPPED' and 'ERROR'. If any gate is SKIPPED, gateResult.passed may be true while passed < total, causing the amber partial-failure branch to render ("2/3 gates passed") even when the server says everything is fine. Fix options:

  • Count non-failing: const failed = checks.filter(c => c.status === 'FAILED' || c.status === 'ERROR').length; const passed = total - failed;
  • Or surface gateResult.summary directly (the server already provides a pre-formatted string)

Important

3. workspacePath null leaves banner stuck (page.tsx ~line 65)

If workspacePath is null when completionStatus first becomes 'completed', the effect aborts early but gateRunning stays false and gateResult stays null — so showGatePending evaluates to true indefinitely. Guard it:

const showGatePending = monitor.completionStatus === 'completed' && !!workspacePath && !gateResult && !gateError;

4. Error silently swallowed in catch (page.tsx ~line 60)

.catch(() => setGateError(true))

The error object is discarded entirely. This makes debugging impossible in DevTools. At minimum:

.catch((err) => {
  console.error('Gates auto-run failed:', err);
  setGateError(true);
})

Nice to Have

  • Issue 5: The gateError message is static. Storing the error detail in state (e.g., gateErrorMsg: string | null) would let you surface a more specific message, consistent with the /review page pattern.
  • Issue 6: The gateRunning || showGatePending overloading at the call site is non-obvious — a short comment would help future contributors.
  • Issue 7: No new tests for the gate auto-run behavior. A GateSummary unit test covering all four states (running, error, passed, partial-failed) would be straightforward.
  • Issue 8: gatesApi.run() is a POST with side effects. The hasRunGatesRef guard prevents double-runs within a single mount but not across navigations. Worth a comment as PROOF9 gates become more expensive.

Block on: Issues 1 and 2. Recommend fixing: Issues 3 and 4. Issues 5–8 are optional polish.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web-ui/src/app/execution/`[taskId]/page.tsx:
- Around line 244-248: The success banner text still tells users to manually
"Run PROOF9 gates", which is stale; update the JSX paragraph that renders
"Execution complete{durationText && ` in ${durationText}`}. Run PROOF9 gates to
verify quality before shipping." (the <p> element containing that string and the
durationText interpolation) to reflect that gates run automatically—e.g., change
to "Execution complete{durationText && ` in ${durationText}`}. PROOF9 gates have
been run automatically to verify quality." or similar concise wording so the
banner no longer implies a manual action.
- Around line 26-30: When calling gatesApi.run() (the code path that updates
gateResult via setGateResult, gateError via setGateError, and gateRunning via
setGateRunning and uses hasRunGatesRef) wrap the async completion handlers with
a local cancelled flag: set cancelled = false before starting the promise, and
in the effect cleanup set cancelled = true; after the gatesApi.run() promise
resolves or rejects, check if cancelled is true before calling setGateResult,
setGateError, or setGateRunning to avoid updating state for a stale taskId; if
the API supports AbortSignal prefer passing an abort token and aborting it in
the cleanup.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d58bd446-fc66-4b1e-83a0-e4d0b66e95d3

📥 Commits

Reviewing files that changed from the base of the PR and between 55860ae and 5d72660.

📒 Files selected for processing (1)
  • web-ui/src/app/execution/[taskId]/page.tsx

Comment on lines +26 to +30
// Gate auto-run state
const [gateResult, setGateResult] = useState<GateResult | null>(null);
const [gateRunning, setGateRunning] = useState(false);
const [gateError, setGateError] = useState(false);
const hasRunGatesRef = useRef(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Next.js App Router, when navigating between two /execution/[taskId]routes handled by the same clientpage.tsx, is local component state preserved across taskId changes by default, or is the page remounted?

💡 Result:

In the Next.js App Router, navigating from /execution/1 to /execution/2 (i.e., changing the dynamic path param in the same [taskId]/page.tsx) will unmount and remount the page subtree, so local component state is not preserved by default. (github.com)

Citations:


🏁 Script executed:

cat -n web-ui/src/app/execution/[taskId]/page.tsx | head -100

Repository: frankbria/codeframe

Length of output: 4593


🏁 Script executed:

find web-ui/src/app/execution -name "layout.tsx" -o -name "layout.ts"

Repository: frankbria/codeframe

Length of output: 45


🏁 Script executed:

cat -n web-ui/src/hooks/useExecutionMonitor.ts

Repository: frankbria/codeframe

Length of output: 7865


Handle stale promise completions from gatesApi.run() when navigating between tasks.

The gatesApi.run() call has no cleanup function, so if you navigate away (changing taskId) before the promise resolves, the old promise will still call setGateResult(), setGateError(), and setGateRunning() on the unmounted component. Add a cleanup function with a cancelled flag to prevent stale state updates.

💡 Suggested fix
   // Auto-run gates non-blocking when execution completes
   useEffect(() => {
     if (monitor.completionStatus !== 'completed' || !workspacePath || hasRunGatesRef.current) return;
     hasRunGatesRef.current = true;
     setGateRunning(true);
+    let cancelled = false;
     gatesApi.run(workspacePath)
-      .then(setGateResult)
-      .catch(() => setGateError(true))
-      .finally(() => setGateRunning(false));
+      .then((result) => {
+        if (!cancelled) setGateResult(result);
+      })
+      .catch(() => {
+        if (!cancelled) setGateError(true);
+      })
+      .finally(() => {
+        if (!cancelled) setGateRunning(false);
+      });
+
+    return () => {
+      cancelled = true;
+    };
   }, [monitor.completionStatus, workspacePath]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/src/app/execution/`[taskId]/page.tsx around lines 26 - 30, When
calling gatesApi.run() (the code path that updates gateResult via setGateResult,
gateError via setGateError, and gateRunning via setGateRunning and uses
hasRunGatesRef) wrap the async completion handlers with a local cancelled flag:
set cancelled = false before starting the promise, and in the effect cleanup set
cancelled = true; after the gatesApi.run() promise resolves or rejects, check if
cancelled is true before calling setGateResult, setGateError, or setGateRunning
to avoid updating state for a stale taskId; if the API supports AbortSignal
prefer passing an abort token and aborting it in the cleanup.

Comment on lines +244 to +248
<div role="alert" className="rounded-lg border border-green-200 bg-green-50 px-4 py-3 dark:border-green-900 dark:bg-green-950/30">
<div className="flex items-center justify-between">
<p className="text-sm font-medium text-green-800 dark:text-green-200">
Execution complete{durationText && ` in ${durationText}`}. Run PROOF9 gates to verify quality before shipping.
</p>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

The completed copy still implies a manual gate run.

This state now auto-runs gates in the background, so “Run PROOF9 gates” is stale and conflicts with the summary rendered underneath.

✏️ Suggested wording
-            Execution complete{durationText && ` in ${durationText}`}. Run PROOF9 gates to verify quality before shipping.
+            Execution complete{durationText && ` in ${durationText}`}. Review the PROOF9 gate summary before shipping.
📝 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
<div role="alert" className="rounded-lg border border-green-200 bg-green-50 px-4 py-3 dark:border-green-900 dark:bg-green-950/30">
<div className="flex items-center justify-between">
<p className="text-sm font-medium text-green-800 dark:text-green-200">
Execution complete{durationText && ` in ${durationText}`}. Run PROOF9 gates to verify quality before shipping.
</p>
<div role="alert" className="rounded-lg border border-green-200 bg-green-50 px-4 py-3 dark:border-green-900 dark:bg-green-950/30">
<div className="flex items-center justify-between">
<p className="text-sm font-medium text-green-800 dark:text-green-200">
Execution complete{durationText && ` in ${durationText}`}. Review the PROOF9 gate summary before shipping.
</p>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/src/app/execution/`[taskId]/page.tsx around lines 244 - 248, The
success banner text still tells users to manually "Run PROOF9 gates", which is
stale; update the JSX paragraph that renders "Execution complete{durationText &&
` in ${durationText}`}. Run PROOF9 gates to verify quality before shipping."
(the <p> element containing that string and the durationText interpolation) to
reflect that gates run automatically—e.g., change to "Execution
complete{durationText && ` in ${durationText}`}. PROOF9 gates have been run
automatically to verify quality." or similar concise wording so the banner no
longer implies a manual action.

@frankbria frankbria merged commit 0a25641 into main Mar 24, 2026
15 checks passed
@frankbria frankbria deleted the feat/issue-472-gate-results-completion-banner branch March 24, 2026 23:28
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.

UX: Show preliminary gate results in execution completion view

1 participant