Skip to content

Release v0.9.2: UI Redesign, Custom LLM Providers, Resumable Reviews & Setup Automation#18

Merged
devarshishimpi merged 38 commits into
mainfrom
dev
Jun 8, 2026
Merged

Release v0.9.2: UI Redesign, Custom LLM Providers, Resumable Reviews & Setup Automation#18
devarshishimpi merged 38 commits into
mainfrom
dev

Conversation

@devarshishimpi

Copy link
Copy Markdown
Owner

Description

This PR introduces the v0.9.2 release, bringing major improvements across the application including a comprehensive UI redesign, support for custom OpenAI-compatible LLM providers, and enhanced review queue reliability.

Key changes and features include:

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Documentation update
  • Chore (refactoring, dependency updates, etc.)

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration.

  • Unit Tests
  • Integration Tests
  • Manual Dashboard Verification
  • Manual GitHub Webhook Verification

Checklist:

  • I have starred Codra on GitHub
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • New and existing unit tests pass locally with my changes
  • I have signed the CLA

…thub-actions

Run full test suite in GitHub Actions with disposable test env
…g-longer-than-expected

Make review queue jobs resumable and lease-aware
…-dashboard

Enhance file review status UI, improve error handling, and strengthen test coverage
…patible-apis

Add dashboard-managed LLM provider system with OpenAI-compatible API support
- Version 0.9.2
- Implement code splitting with React.lazy for all pages
- Add Suspense fallbacks for async route components
- Redesign landing page (hero, sign-in, feature layout)
- Redesign login page (cleaner UI, security note)
- Minor app-shell spacing adjustment
…il fetches

- pemToArrayBuffer: strip literal \\n escape sequences before atob()
  to handle keys pasted as single-line strings in wrangler secrets
- api.ts: cache the updates-email status promise so the /api/auth/updates-email
  endpoint is only called once per page session, reducing KV reads
…are-deployment-and-onboarding-with-nodejs-script

Feat: Automate Cloudflare setup, redesign UI, and optimize with code splitting
codra-app-personal[bot]

This comment was marked as resolved.

codra-app-personal[bot]

This comment was marked as resolved.

codra-app-personal[bot]

This comment was marked as resolved.

@devarshishimpi devarshishimpi merged commit bf9f4b7 into main Jun 8, 2026
3 of 4 checks passed

@codra-app-personal codra-app-personal Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Codra Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 16dda1a229

ℹ️ About Codra in GitHub

Your team has set up Codra to review pull requests in this repo. Reviews are triggered when you:

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codra-app review"

If Codra has suggestions, it will comment; otherwise it will react with 👍.

Codra can also answer questions or update the PR. Try commenting "@codra-app address that feedback".

Note

69 comments were omitted from this review to reduce noise and respect the configured max_comments limit (10). Showing the most critical issues.

@@ -8,84 +8,51 @@ interface LiveReviewStepperProps {
export function LiveReviewStepper({ job, compact = true }: LiveReviewStepperProps) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 Stepper visualization removed

The diff completely removes the logic for stepStates and the rendering of the 4-step stepper dots. Users can no longer see the progress through the review process (Queued -> Scanning -> Analyzing -> Done), only the current status label. This is a significant functional regression.

Suggested change
export function LiveReviewStepper({ job, compact = true }: LiveReviewStepperProps) {
Restore the `stepStates` array and the mapping logic to determine the state of each step, and render the step dots in the JSX.

className="flex items-center gap-3 text-xl md:text-2xl font-bold text-foreground"
style={{ letterSpacing: '-0.025em' }}
>
{title}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 ReferenceError: props is not defined

The PageHeader component uses destructuring in its function signature (line 13: export function PageHeader({), which means the props object is not available as a variable within the component scope. Attempting to access props.versionBadge on line 36 will cause a runtime ReferenceError, crashing the component.

Suggested change
{title}
{versionBadge && (
<span className="rounded-full bg-primary/10 px-2.5 py-0.5 text-sm font-semibold text-primary">
v{versionBadge}
</span>
)}

@@ -9,7 +9,7 @@ export interface SwitchProps extends Omit<React.InputHTMLAttributes<HTMLInputEle
const Switch = React.forwardRef<HTMLInputElement, SwitchProps>(
({ className, onCheckedChange, onChange, ...props }, ref) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 Event handlers swallowed by destructuring

The onChange and onCheckedChange props are destructured from the component arguments on line 10 but are not passed to the underlying <input> element. Because they are removed from the props object before it is spread onto the input ({...props} on line 21), the Switch component will not trigger any callbacks when its state changes, rendering it non-functional.

Suggested change
({ className, onCheckedChange, onChange, ...props }, ref) => {
({ className, onCheckedChange, ...props }, ref) => {
return (
<label className="relative inline-flex cursor-pointer items-center">
<input
type="checkbox"
className="sr-only peer"
onChange={(e) => {
onCheckedChange?.(e.target.checked);
props.onChange?.(e);
}}
{...props}
/>
<div className={cn(
"relative h-5 w-9 rounded-full border transition-all duration-200",
"border-border bg-muted-foreground/20",
"peer-checked:border-primary peer-checked:bg-primary",
"after:absolute after:left-[3px] after:top-[3px] after:h-3 after:w-3 after:rounded-full after:bg-white after:shadow-sm after:transition-transform after:duration-200 after:content-['']",
"peer-checked:after:translate-x-4",
className
)} />
</label>
);
}

repository: { name: repoName, owner: { login: 'test-owner' } }
});
rawPayload.pull_request.head.sha = 'c'.repeat(40);
rawPayload.pull_request.base.sha = 'd'.repeat(40);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 Missing import for signPayload

The new test case 'rejects GitHub webhooks posted to the site root' calls signPayload on line 131, but this function is not imported from './helpers' or any other module in the provided imports. This will result in a ReferenceError when the test is executed.

Suggested change
rawPayload.pull_request.base.sha = 'd'.repeat(40);
import { createMockPRWebhook, createTestEnv, signPayload } from './helpers';

return privateRanges.some((regex) => regex.test(hostname));
}

function isValidPublicUrl(urlString: string) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 SSRF vulnerability via DNS Rebinding

The isValidPublicUrl function performs a check on the hostname before making the request. This is vulnerable to DNS Rebinding attacks: an attacker can provide a domain they control that resolves to a public IP during the validation phase but resolves to a private internal IP (e.g., 127.0.0.1) when fetch is actually called. To prevent this, the application should resolve the hostname to an IP address once and validate that IP before establishing the connection.

Suggested change
function isValidPublicUrl(urlString: string) {
// Use a library or custom resolver to resolve DNS first, then validate the resulting IP address
const ip = await dns.resolve(url.hostname);
if (isPrivateIP(ip)) throw new Error('SSRF detected');
await fetch(`http://${ip}/...`, { headers: { 'Host': url.hostname } });

FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'jobs'
AND column_name = 'commit_sha';

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 Incomplete migration logic for commit_sha and base_sha conversion

The diff shows the beginning of a complex conversion from TEXT to BYTEA for 'commit_sha' and 'base_sha'. This pattern (adding a new column, updating, dropping the old, renaming the new) is risky in a single migration if it fails halfway. Furthermore, the logic for 'decode(commit_sha, 'hex')' assumes valid hex strings, which is handled by a regex, but any failure in the subsequent 'DROP' or 'RENAME' will leave the database in an inconsistent state.

Comment thread src/client/app.css
}

.codra-toast-close:hover {
background: oklch(82% 0.010 115) !important;

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 Hardcoded colors in Toast overrides break theme maintainability

The new Sonner Toast styles (lines 884-1058) use hardcoded oklch values for success, error, warning, and info states. This bypasses the design tokens defined in :root (e.g., --success, --danger, --warning). If the primary theme colors are updated in the root, the toasts will remain unchanged, creating a visual inconsistency and increasing maintenance overhead.

Suggested change
background: oklch(82% 0.010 115) !important;
.codra-toast-success {
background: var(--success-bg) !important;
border-color: var(--success-border) !important;
color: var(--success) !important;
}

<div className="space-y-3">
{tiers.map((tier, index) => (
<section key={index} className="rounded-md border border-border bg-muted/5 p-4">
<div className="mb-4 flex items-start justify-between gap-3">

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 Use unique keys for list items

The tiers.map loop uses the array index as the key prop. This is a React anti-pattern that can lead to stale state, incorrect updates, and performance degradation if the list is reordered or filtered. The tier object should have a unique identifier (e.g., id) to serve as the key.

Suggested change
<div className="mb-4 flex items-start justify-between gap-3">
key={tier.id || index} (if 'id' exists) or generate a unique ID.

Comment thread src/client/lib/api.ts
return request<JobDetailResponse>(`/api/jobs/${id}`);
getJob(id: string, options: { etag?: string | null } = {}) {
const headers = new Headers();
if (options.etag) {

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 Breaking change in getJob return type

The return type of api.getJob has changed from Promise<JobDetailResponse> to a metadata object Promise<{ status: number, etag: string | null, lastModified: string | null, notModified: boolean, data?: JobDetailResponse }>. This is because it now uses requestWithMeta instead of request. Any existing UI components calling getJob and expecting the job data directly will now receive this wrapper object, likely leading to runtime errors (e.g., attempting to access properties of the job on the metadata object).

Suggested change
if (options.etag) {
// If metadata is required, the return type should be explicitly defined in the api object
// and callers updated. Otherwise, use request() or wrap requestWithMeta to return just the data.
return requestWithMeta<JobDetailResponse>(`/api/jobs/${id}`, { headers }).then(res => res.data);

</p>
</div>

<a href="/auth/github">

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 Invalid nested interactive elements

The code wraps a <button> element inside an <a> tag (lines 69-74). This is invalid HTML and creates accessibility issues, as screen readers and keyboards may struggle to determine which element is being interacted with. It can also lead to inconsistent behavior across browsers.

Suggested change
<a href="/auth/github">
<a
href="/auth/github"
className="inline-flex items-center gap-2.5 h-10 px-5 bg-foreground text-background text-sm font-semibold rounded-lg hover:bg-foreground/90 active:scale-[0.98] transition-all"
>
Get started with GitHub
<ArrowRight size={15} />
</a>

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