Skip to content

Feat/tool integration v2 - #86

Open
Amazing-Stardom wants to merge 16 commits into
masterfrom
feat/tool-integration-v2
Open

Feat/tool integration v2#86
Amazing-Stardom wants to merge 16 commits into
masterfrom
feat/tool-integration-v2

Conversation

@Amazing-Stardom

Copy link
Copy Markdown
Contributor

No description provided.

}
if (prMrUrl) {
const url = prMrUrl.toLowerCase();
if (url.includes('github.com')) return 'github';
@lovestaco
lovestaco marked this pull request as ready for review August 29, 2026 16:13
@LiveReview-Bot

Copy link
Copy Markdown

Implement Third-Party Tool Integration

Overview

This change introduces a new feature enabling third-party static analysis tools to run as Lambda-backed jobs during LiveReview code reviews. It establishes a comprehensive system including new database schemas, API endpoints, UI components, and job orchestration. The system aims to provide detailed tool findings and credit consumption within the review process.

Technical Highlights

  • db/migrations/*, db/schema.sql: Add available_tools, org_tools, org_tool_billing_state, and tool_credit_ledger tables.
  • internal/api/server.go, tools_handler.go: Implement new API endpoints for managing global and organization-specific tools.
  • internal/review_processor/events.go: Introduce SeverityCounts and ToolSummary structs for review event aggregation.
  • docs/tools/tools-integration-beta.md: Defines the tool_invocation River job for orchestrating Lambda-based tool execution.
  • ui/src/components/reviews/ToolAnalysisCard.tsx: New UI component displays detailed tool analysis results with filtering and pagination.
  • ui/src/pages/Reviews/ReviewDetail.tsx: Integrates ToolAnalysisCard and processes tool accounting data for display.
  • internal/api/diff_review.go: Gracefully handles decoding failures for reviews without AI comments.
  • ui/src/components/Dashboard/widgets/ToolsUsageWidget.tsx: Adds a new dashboard widget for tool usage overview.

Impact

  • Functionality: Users can now integrate, configure, and view results from third-party static analysis tools directly within code reviews. Review summaries include detailed tool findings and severity counts.
  • Risk: New database schemas require careful migration and data integrity checks. The hasNoReviewLayerData function in ReviewLayersData.tsx now always returns false, potentially affecting existing dashboard logic. Extensive mock data usage in ReviewDetail.tsx could mask real API issues during development.


1. THE System SHALL provide an `available_tools` table with columns: `id` (bigserial primary key), `name` (text not null unique), `description` (text not null), and `lambda_arn` (text not null).
2. THE System SHALL seed the `available_tools` table with at least two initial rows: one for `ruff` and one for `pylint`, each with a non-empty `description` and a placeholder `lambda_arn`.
3. THE System SHALL manage the `available_tools` schema exclusively through dbmate migration files located in `db/migrations/`, and SHALL NOT apply these migrations directly to any production database.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: critical

The dbmate migrations have not been applied to the production environment. The strategy for updating the schema needs to be clarified.

Suggestions:

  1. Clarify how production database schema updates will be managed if dbmate migrations are not applied directly.
  2. Reconcile this statement with standard dbmate usage for all environments.


1. THE System SHALL provide an `org_tools` table with columns: `org_id` (bigint not null, references `organizations.id`), `tool_id` (bigint not null, references `available_tools.id`), `enabled` (boolean not null default false), `config_json` (jsonb not null default `'{}'`), and a composite primary key on (`org_id`, `tool_id`).
2. THE System SHALL enforce that every row in `org_tools` references a valid `org_id` in the `organizations` table and a valid `tool_id` in the `available_tools` table via foreign key constraints.
3. THE System SHALL manage the `org_tools` schema exclusively through dbmate migration files and SHALL NOT apply these migrations directly to any production database.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: critical

The dbmate migrations have not been applied to the production environment. The strategy for updating the schema needs to be clarified.

Suggestions:

  1. Clarify how production database schema updates will be managed if dbmate migrations are not applied directly.
  2. Reconcile this statement with standard dbmate usage for all environments.

1. WHEN `isCloudMode()` returns `true` AND the authenticated user's role is `owner`, THE Settings Page SHALL render a navigable tab at the hash route `third-party-tools` within `/#/settings`.
2. WHEN `isCloudMode()` returns `false`, THE Settings Page SHALL NOT render the `third-party-tools` tab.
3. WHEN the authenticated user's role is not `owner`, THE Settings Page SHALL NOT render the `third-party-tools` tab as a clickble navigation item.
4. WHEN a non-owner org member navigates directly to `/#/settings#third-party-tools`, THE Settings Page SHALL render a read-only view of the enabled tools without controls to modify them.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

A read-only view should be implemented for non-owners. Ensure that no sensitive configuration information is exposed through this view.

Suggestions:

  1. Verify that the read-only view for non-owners does not expose any sensitive configuration details that should be restricted.
  2. Implement robust data filtering on the backend for read-only access.

id bigserial PRIMARY KEY,
name text NOT NULL UNIQUE,
description text NOT NULL,
lambda_arn text NOT NULL,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

The lambda_arn column is defined as NOT NULL, but the seeding process requires placeholders. This needs to be reconciled with requirement R1.2.

Suggestions:

  1. Clarify how lambda_arn will be handled for initial seeded tools (R1.2) if they are NOT NULL and 'no hardcoded ARNs belong here'.
  2. Consider allowing NULL initially or ensuring a robust placeholder/update mechanism.

name text NOT NULL UNIQUE,
description text NOT NULL,
lambda_arn text NOT NULL,
multiplier numeric(6,2) NOT NULL DEFAULT 1.0,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

A multiplier column has been added. Its purpose is not clear from the schema definition.

Suggestions:

  1. Add a comment or documentation explaining the purpose and usage of the multiplier column.

description text NOT NULL,
lambda_arn text NOT NULL,
multiplier numeric(6,2) NOT NULL DEFAULT 1.0,
use_case text NOT NULL DEFAULT '',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

A use_case column has been added. Its purpose is not clear from the schema definition.

Suggestions:

  1. Add a comment or documentation explaining the purpose and usage of the use_case column.

created_at timestamptz NOT NULL DEFAULT now()
);

-- Tools are registered via the lr-tools deployer's `register-tools` command,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: critical

This comment conflicts with requirement R1.2, which specifies seeding with placeholder ARNs.

Suggestions:

  1. Reconcile this comment with Requirement 1.2, which states that available_tools should be seeded with placeholder lambda_arns.
  2. Clarify if seeding happens outside of this migration or if placeholders are acceptable here.

@@ -0,0 +1,15 @@
-- migrate:up
CREATE TABLE IF NOT EXISTS public.org_tools (
org_id bigint NOT NULL REFERENCES public.orgs(id) ON DELETE CASCADE,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

The ON DELETE CASCADE option is used for the orgs table. Please confirm that this aligns with the business logic requirements.

Suggestions:

  1. Confirm that cascading deletion of org_tools entries upon orgs deletion aligns with business requirements.
  2. Consider ON DELETE RESTRICT or SET NULL if org_tools data needs to be preserved or handled differently.

-- migrate:up
CREATE TABLE IF NOT EXISTS public.org_tools (
org_id bigint NOT NULL REFERENCES public.orgs(id) ON DELETE CASCADE,
tool_id bigint NOT NULL REFERENCES public.available_tools(id) ON DELETE CASCADE,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

The ON DELETE CASCADE option is used for the available_tools table. Please confirm that this aligns with the business logic requirements.

Suggestions:

  1. Confirm that cascading deletion of org_tools entries upon available_tools deletion aligns with business requirements.
  2. This is generally acceptable, as configurations for a non-existent tool are usually irrelevant.

tool_id bigint NOT NULL REFERENCES public.available_tools(id) ON DELETE CASCADE,
enabled boolean NOT NULL DEFAULT false,
config_json jsonb NOT NULL DEFAULT '{}',
updated_at timestamptz NOT NULL DEFAULT now(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

The updated_at column lacks an ON UPDATE trigger, meaning it is only set upon insertion and not updated thereafter.

Suggestions:

  1. Add an ON UPDATE trigger to automatically update the updated_at timestamp whenever a row in org_tools is modified.
  2. Consider using the org_tool_billing_state_set_updated_at() function if applicable.

Comment thread db/schema.sql
@@ -1,7 +1,7 @@
\restrict dbmate

-- Dumped from database version 15.17 (Debian 15.17-1.pgdg13+1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

There is a PostgreSQL version bump from 15.17 to 16.14. Please ensure compatibility with existing applications and queries.

Suggestions:

  1. Verify compatibility of all application components and dependencies with PostgreSQL 16.14.
  2. Ensure all environments (dev, staging, prod) are aligned or tested for this version.

Comment thread db/schema.sql
-- Name: org_tool_billing_state_set_updated_at(); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.org_tool_billing_state_set_updated_at() RETURNS trigger

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

The function org_tool_billing_state_set_updated_at() is defined but appears to be unused.

Suggestions:

  1. Either add an ON UPDATE trigger to a relevant table (e.g., org_tools) to use this function, or remove the function if it's not needed.
  2. If intended for a future table, add a comment indicating its purpose and expected usage.


1. Load the diff from `SELECT diff FROM reviews WHERE id = $1 AND org_id = $2`. If the review has no diff, log and return without error (nothing to analyse).
2. POST the diff as the Lambda payload to the tool's `lambda_arn` via HTTPS.
3. On non-2xx response: return an error so River applies its standard retry policy.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

River retries requests on non-2xx responses. Ensure that a maximum retry limit is configured to prevent infinite retries for permanent errors.

Suggestions:

  1. Implement a maximum retry limit for River jobs to prevent indefinite retries on non-transient Lambda errors.

In `WebhookOrchestratorV2` (or the unified processor), after diff extraction completes:

```go
enabledTools, err := store.GetEnabledToolsForOrg(ctx, orgID)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: critical

For the fan-out trigger, consider performing credit checks before inserting jobs. This would prevent queuing jobs for organizations that have insufficient credits or would exceed their budget.

Suggestions:

  1. Implement a credit budget check before inserting ToolInvocationJobArgs into River to prevent unnecessary job execution and potential cost overruns for organizations.

In `WebhookOrchestratorV2` (or the unified processor), after diff extraction completes:

```go
enabledTools, err := store.GetEnabledToolsForOrg(ctx, orgID)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

If GetEnabledToolsForOrg is slow or returns a large number of tools, this loop could become a performance bottleneck. Ensure its efficiency.

Suggestions:

  1. Optimize GetEnabledToolsForOrg to ensure it's efficient, especially for organizations with many enabled tools, to avoid performance bottlenecks during job fan-out.


result, err := decodeReviewResult(meta)
if err != nil {
return JSONErrorWithEnvelope(c, http.StatusInternalServerError, fmt.Sprintf("failed to decode review result: %v", err))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

All decode errors are being swallowed, which can hide data corruption or invalid JSON.

Suggestions:

  1. Differentiate empty/missing metadata from actual JSON parsing errors.
  2. Log the decoding error to prevent silent failures.

return nil, fmt.Errorf("failed to count batch IDs: %w", err)
}

sevCounts, err := s.repo.GetSeverityCounts(ctx, reviewID, orgID)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

The error from GetSeverityCounts is being swallowed, which can hide query or database failures.

Suggestions:

  1. Log the error to assist with debugging database or query issues.

sevCounts = SeverityCounts{}
}

toolSum, err := s.repo.GetToolSummary(ctx, reviewID, orgID)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

The error from GetToolSummary is being swallowed, which can hide query or database failures.

Suggestions:

  1. Log the error to assist with debugging database or query issues.

dbURL = os.Getenv("DATABASE_URL")
}
if dbURL == "" {
dbURL = "postgres://livereview:livereview_password_123@localhost:5432/livereview?sslmode=disable"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

A hardcoded fallback for the database URL is present. Consider using a dedicated test database setup or an in-memory database for better isolation during testing.

Suggestions:

  1. Use a test-specific database configuration that doesn't rely on a default hardcoded value, especially one with a default password.
  2. For unit tests, consider using an in-memory database like SQLite for faster and more isolated execution.

ReviewID: reviewID,
OrgID: orgID,
EventType: "tool_dispatch",
Data: []byte(`{"tool_id": 1, "tool_name": "ruff", "status": "pending"}`),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

Tool event data is hardcoded. It is recommended to use constants or test data structures for improved clarity.

Suggestions:

  1. Define test data for tool_dispatch and tool_result events as Go structs or constants to improve readability and maintainability.

func (s *Server) UpsertAvailableTool(c echo.Context) error {
var req UpsertToolRequest
if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request body"})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

A generic error message is being returned. It would be beneficial to return a specific binding error for improved debugging.

Suggestions:

  1. Return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()}) to provide more specific details about the binding failure.

if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request body"})
}
if req.Name == "" || req.LambdaARN == "" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

Basic validation is performed. Consider using a regular expression for the LambdaARN format to ensure correctness.

Suggestions:

  1. Add more robust validation for LambdaARN using a regular expression to ensure it matches expected AWS ARN format.
  2. Consider adding length limits to Name and Description fields.

tools := make([]ToolRow, 0)
for rows.Next() {
var t ToolRow
if err := rows.Scan(&t.ID, &t.Name, &t.Description, &t.LambdaARN, &t.Multiplier, &t.UseCase); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

Error handling within the loop can lead to the process stopping upon encountering the first scan error.

Suggestions:

  1. Consider logging the specific row data that caused the scan error for better debugging, if possible without exposing sensitive data.

var req struct {
Enabled *bool `json:"enabled"`
}
if err := c.Bind(&req); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

A generic error message is being returned. It would be beneficial to return a specific binding error for improved debugging.

Suggestions:

  1. Return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()}) to provide more specific details about the binding failure.

`
rows, err := r.db.QueryContext(ctx, query, reviewID, orgID)
if err != nil {
return SeverityCounts{}, fmt.Errorf("failed to query severity counts: %w", err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

The error message 'failed to query severity counts: %w' is logged. Consider adding reviewID or orgID to the log for better context.

Suggestions:

  1. Include reviewID and orgID in the error message for better debugging context.

var lvl string
var cnt int
if err := rows.Scan(&lvl, &cnt); err != nil {
return SeverityCounts{}, err

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: critical

An unhandled scan error occurred. This could potentially lead to returning partial counts or corrupt data.

Suggestions:

  1. Log the specific error from rows.Scan to understand why it failed.
  2. Consider returning the error immediately or handling it more robustly to prevent partial or incorrect SeverityCounts.

ORDER BY ts ASC
`
rows, err := r.db.QueryContext(ctx, query, reviewID, orgID)
if err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

The error message 'failed to query tool summary: %w' is logged. Consider adding reviewID or orgID to the log for better context.

Suggestions:

  1. Include reviewID and orgID in the error message for better debugging context.

for rows.Next() {
var ev rawToolEvent
if err := rows.Scan(&ev.EventType, &ev.Data, &ev.CreatedAt); err != nil {
return nil, err

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: critical

An unhandled scan error occurred. This could potentially lead to returning partial tool events or corrupt data.

Suggestions:

  1. Log the specific error from rows.Scan to understand why it failed.
  2. Consider returning the error immediately or handling it more robustly to prevent partial or incorrect toolEvents.

for _, ev := range toolEvents {
if ev.EventType == "tool_dispatch" {
var d toolDispatchData
if err := json.Unmarshal(ev.Data, &d); err == nil && d.ToolName != "" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

Tool dispatch events with unmarshal errors or empty tool names are silently skipped.

Suggestions:

  1. Log unmarshal errors for tool_dispatch events to identify malformed data.
  2. Consider if d.ToolName == "" should be an error or simply skipped.

}
} else if ev.EventType == "tool_result" {
var res toolResultData
if err := json.Unmarshal(ev.Data, &res); err == nil && res.ToolName != "" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

Tool result events with unmarshal errors or empty tool names are silently skipped.

Suggestions:

  1. Log unmarshal errors for tool_result events to identify malformed data.
  2. Consider if res.ToolName == "" should be an error or simply skipped.

}

// Fetch multipliers from available_tools table in database
multiplierMap := make(map[string]float64)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

Multiplier query errors are silently ignored, resulting in a default multiplier of 1.0 being used.

Suggestions:

  1. Log tErr if fetching multipliers fails to understand why.
  2. Consider if silently defaulting to 1.0 is the desired behavior for cost calculation, or if it should be more explicit (e.g., return an error or set TotalCostUsd to nil).

for tRows.Next() {
var tName string
var mult float64
if err := tRows.Scan(&tName, &mult); err == nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

Multiplier scan errors are being silently ignored.

Suggestions:

  1. Log the err from tRows.Scan to identify issues with available_tools data.

res, hasResult := resultsMap[toolName]
dispatchTime := dispatchedMap[toolName]

mult, exists := multiplierMap[strings.ToLower(toolName)]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

Cost calculation defaults to a multiplier of 1.0 if the multiplier is not found or is invalid.

Suggestions:

  1. Confirm if defaulting to 1.0 for CreditsUsed and TotalCostUsd is the correct business logic when a multiplier is missing or invalid. This could lead to incorrect billing/accounting state.
  2. Consider logging a warning when a multiplier is not found for a tool.

item.Status = "clean"
}
} else {
if time.Since(dispatchTime) > 10*time.Second {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

A 10-second heuristic is used to differentiate between 'running' and 'pending' statuses, which may lead to inaccuracies.

Suggestions:

  1. This heuristic might misclassify tools if processing takes longer or events are delayed. Consider a more robust mechanism, like a dedicated 'tool_running' event or a configurable timeout.
  2. Document this heuristic clearly if it's intended behavior.


sevCounts, err := s.repo.GetSeverityCounts(ctx, reviewID, orgID)
if err != nil {
// Log or ignore non-critical severity error

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

The error from GetSeverityCounts is being silently ignored. Logging is needed to capture these errors.

Suggestions:

  1. Log the error from s.repo.GetSeverityCounts to aid debugging, even if sevCounts defaults to empty.

}

toolSum, err := s.repo.GetToolSummary(ctx, reviewID, orgID)
if err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

The error from GetToolSummary is being silently ignored. Logging is needed to capture these errors.

Suggestions:

  1. Log the error from s.repo.GetToolSummary to aid debugging, even if toolSum defaults to nil.

}

// UpsertOrgTool inserts or updates the enabling configuration of a tool for a specific organization.
func (s *ToolsStore) UpsertOrgTool(ctx context.Context, orgID, toolID int64, enabled bool) (OrgToolRow, error) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

In UpsertOrgTool, the config_json is not updated when a conflict occurs.

Suggestions:

  1. If config_json should be updatable via this function, add config_json = EXCLUDED.config_json to the ON CONFLICT clause.
  2. If config_json is intended to be static after initial insert, add a comment explaining this design choice.


// UpsertOrgTool inserts or updates the enabling configuration of a tool for a specific organization.
func (s *ToolsStore) UpsertOrgTool(ctx context.Context, orgID, toolID int64, enabled bool) (OrgToolRow, error) {
// First check if the tool actually exists in available_tools

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

A check for the existence of a tool appears redundant if tool_id is a foreign key.

Suggestions:

  1. If org_tools.tool_id has a foreign key constraint to available_tools.id, remove this SELECT EXISTS query. The INSERT will fail with an FK violation if the tool does not exist, which can be handled.
  2. If no FK exists, consider adding one for data integrity.

return OrgToolRow{}, fmt.Errorf("failed to check tool existence: %w", err)
}
if !exists {
return OrgToolRow{}, sql.ErrNoRows

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: info

The function is returning sql.ErrNoRows for a non-existent tool, which might not be the intended behavior.

Suggestions:

  1. Consider defining a custom error type (e.g., ErrToolNotFound) for better semantic clarity for callers, rather than reusing sql.ErrNoRows which typically implies no rows were returned from a query, not that an entity doesn't exist.

// Backend always returns one row per known layer, even at all-zero — treat that as fallback trigger for demo mode.
export function hasNoReviewLayerData(layers: ReviewLayer[]): boolean {
return layers.length === 0 || layers.every((layer) => layer.reviews_run === 0);
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: critical

This function always returns false, which appears to break its original intended functionality.

Suggestions:

  1. Revert to original logic or rename function to reflect new behavior.
  2. If intent is to force demo mode, apply this logic at the call site, not within the utility function.

const [reviewLayers, setReviewLayers] = useState<ReviewLayers | null>(null);
const [loading, setLoading] = useState(true);
const [reviewLayers, setReviewLayers] = useState<ReviewLayers | null>(MOCK_REVIEW_LAYERS_OBJECT);
const [loading, setLoading] = useState(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: warning

The loading variable is initialized to false. This might prevent the loading state from being displayed correctly.

Suggestions:

  1. Initialize loading to true if a loading indicator is desired while data fetches.
  2. Consider if showing mock data immediately is sufficient without a separate loading state.

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.

4 participants