Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7e41598
chore: update dependencies and improve error logging
devarshishimpi Jul 15, 2026
993af70
add: redesign stats and about component, update theme handling, and e…
devarshishimpi Jul 16, 2026
9ebc5cd
add: migrate to base ui with new look on llm provider job tables and …
devarshishimpi Jul 16, 2026
f93be3f
fix: telemetry and repos ui
devarshishimpi Jul 16, 2026
6eb0808
refactor: update contributing guidelines and improve UI components
devarshishimpi Jul 18, 2026
3c3380d
feat: enhance button styles and add session caching for job details
devarshishimpi Jul 18, 2026
75136c7
fix: improve issues accuracy and files reviewed ui changes
devarshishimpi Jul 19, 2026
3115e19
add: accounts settings route
devarshishimpi Jul 22, 2026
59a5ce2
feat: refresh job review UI and add timezone-aware account stats
devarshishimpi Aug 1, 2026
09330f9
merge: resolve dependency conflicts from main into feature/pr-diff-ui
devarshishimpi Aug 1, 2026
ef81807
feat: enhance GitHub review comment handling with line and side addre…
devarshishimpi Aug 1, 2026
b164e0c
feat: add xAI provider to llm_providers and update related configurat…
devarshishimpi Aug 2, 2026
a7bd5aa
feat: add confidence_score to review_comments and update related data…
devarshishimpi Aug 2, 2026
aa79fd1
feat: enhance findings reporting by calculating total findings and im…
devarshishimpi Aug 2, 2026
7485d21
feat: ground findings in evidence, suppress re-posts
devarshishimpi Aug 2, 2026
e251766
fix: keep large reviews inside the subrequest budget
devarshishimpi Aug 2, 2026
eec7d57
feat: label finding types, track disposition, drop merged React persona
devarshishimpi Aug 3, 2026
260738c
feat: enforce claim-type denylist, add a Gatekeeper verifier, and hum…
devarshishimpi Aug 3, 2026
e29cfd2
feat: add deterministic rule channel, Vertex provider, SSRF guard, an…
devarshishimpi Aug 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ updates:
directory: "/"
schedule:
interval: "weekly"
cooldown:

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 configuration for Dependabot schedule

The 'cooldown' key is not a valid property within the 'updates' configuration block for Dependabot. Dependabot schema expects 'schedule' to define frequency, and there is no global or package-ecosystem level 'cooldown' parameter supported in the standard .github/dependabot.yml schema. This configuration will cause the Dependabot workflow to fail to parse the file.

Suggested change
cooldown:
Remove the 'cooldown' block as it is not a supported schema property.

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 Dependabot configuration schema

The 'cooldown' key is not a valid configuration option for the 'updates' block in Dependabot. This will cause the Dependabot configuration to fail validation, preventing automated dependency updates.

Suggested change
cooldown:
Remove the 'cooldown' blocks as they are not supported by the Dependabot configuration schema.

default-days: 7

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
cooldown:
default-days: 7
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.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.

P0 Use of non-existent GitHub Action versions

The diff updates actions/checkout and actions/setup-node to version v7.0.0. These versions do not exist as of current date; the official latest releases for these actions are v4.x. Using non-existent tags or SHA-like tags that don't map to valid releases can cause CI pipeline failures if the runner cannot resolve the action.

Suggested change
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
Revert to valid versions: actions/checkout@v4 and actions/setup-node@v4.

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 Use of invalid or non-existent GitHub Action versions

The diff updates actions/checkout to version v7.0.0 and actions/setup-node to v7.0.0. As of now, actions/checkout is at v4 and actions/setup-node is at v4. Using v7.0.0 is likely a configuration error and will cause the CI pipeline to fail because these tags do not exist in the official repositories.

Suggested change
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
Revert to the stable v4 versions: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 (v4.2.2) and actions/setup-node@39370e36706b68512530c0c66060c1737e6f8279 (v4.2.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.

P0 Invalid GitHub Action Version

The diff attempts to use version 'v7.0.0' for 'actions/checkout' and 'actions/setup-node'. As of current GitHub Actions releases, both 'actions/checkout' and 'actions/setup-node' are on major version 4. There is no v7.0.0 for these official actions, which will cause the CI workflow to fail because the specified action version does not exist.

Suggested change
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
Revert to established versions, e.g., actions/checkout@v4.2.2 and actions/setup-node@v4.4.0.


- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.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.

P0 Invalid GitHub Action version

The specified version 'v7.0.0' for 'actions/setup-node' does not exist. The latest major version is v4. Using a non-existent tag will cause the CI workflow to fail.

Suggested change
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0

with:
node-version: 20
cache: 'npm'
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.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.

P1 Invalid version for actions/checkout

The diff specifies 'actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0' labeled as 'v7.0.0'. As of current releases, the latest major version for actions/checkout is v4. Version v7.0.0 does not exist and may cause the workflow to fail if it points to an invalid tag, branch, or commit that does not align with the action's intended repository state.

Suggested change
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

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 Invalid Action Version Usage

The diff attempts to upgrade actions/checkout to version v7.0.0 and github/codeql-action to v4.37.0. These versions do not exist in the official GitHub repositories as of current date. Using non-existent tags will cause the CI workflow to fail because the action runner will be unable to resolve the specified git reference.

Suggested change
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
Revert to the latest valid stable versions, e.g., actions/checkout@v4 and github/codeql-action@v3.

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 Invalid version for actions/checkout

The diff specifies 'actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0'. As of current GitHub Actions releases, 'actions/checkout' version 'v7.0.0' does not exist. The latest major version is v4. Specifying a non-existent tag will cause the workflow to fail to resolve the action.

Suggested change
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2


- name: Initialize CodeQL
uses: github/codeql-action/init@411c4c9a36b3fca4d674f06b6396b2c6d23522c6 # v3.36.3
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
with:
languages: ${{ matrix.language }}

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@411c4c9a36b3fca4d674f06b6396b2c6d23522c6 # v3.36.3
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,4 @@ vite.config.ts.timestamp-*
.wrangler

.agent

6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ npm run typecheck

## 🚀 Pull Request Process

1. **Fork & Branch**: Create a feature branch from `dev`.
1. **Fork & Branch**: Create a feature branch from `main`.
2. **Atomic Commits**: Keep your commits focused and descriptive.
3. **Sync**: Ensure your branch is up to date with `dev`.
4. **Target Branch**: Open pull requests against `dev`.
3. **Sync**: Ensure your branch is up to date with `main`.
4. **Target Branch**: Open pull requests against `main`.
5. **PR Description**: Use the provided template (if available) or clearly explain the *what* and *why* of your changes.
6. **CLA Check**: Once you open the PR, an automated check will verify your CLA status. If you haven't signed yet, follow the link in the check output.

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ The full setup and operations guides live at [codra.run/docs](https://codra.run/

## Contributing

Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request against `dev`. Codra uses a Contributor License Agreement for contributions.
Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request against `main`. Codra uses a Contributor License Agreement for contributions.

## License

Expand Down
102 changes: 102 additions & 0 deletions db/migrations/003_grounding.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
CREATE TABLE IF NOT EXISTS account_settings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
github_user_id BIGINT NOT NULL UNIQUE,
github_username TEXT NOT NULL,
account_name TEXT,
account_email TEXT,

timezone TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

ALTER TABLE account_settings ADD COLUMN IF NOT EXISTS timezone TEXT;

ALTER TABLE review_comments ADD COLUMN IF NOT EXISTS confidence_score REAL;

ALTER TABLE review_comments ADD COLUMN IF NOT EXISTS evidence TEXT COMPRESSION lz4;
ALTER TABLE review_comments ADD COLUMN IF NOT EXISTS fingerprint TEXT;
ALTER TABLE review_comments ADD COLUMN IF NOT EXISTS anchor_hash TEXT;
ALTER TABLE review_comments ADD COLUMN IF NOT EXISTS posted BOOLEAN NOT NULL DEFAULT FALSE;

CREATE INDEX IF NOT EXISTS review_comments_posted_fingerprint_idx
ON review_comments (file_review_id, fingerprint)
WHERE posted AND fingerprint IS NOT NULL;

CREATE TABLE IF NOT EXISTS comment_feedback (
id BIGSERIAL PRIMARY KEY,
repository_id INTEGER NOT NULL REFERENCES repositories(id) ON DELETE CASCADE,
pr_number INTEGER,
fingerprint TEXT NOT NULL,
anchor_hash TEXT,
github_comment_id BIGINT NOT NULL,
outcome TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE UNIQUE INDEX IF NOT EXISTS comment_feedback_unique_idx
ON comment_feedback (repository_id, github_comment_id, outcome);

CREATE INDEX IF NOT EXISTS comment_feedback_repo_outcome_idx
ON comment_feedback (repository_id, outcome);

DO $backfill$
BEGIN
IF EXISTS (
SELECT 1 FROM schema_migrations
WHERE name IN (
'003_clear_stored_diff_input.sql',
'003_diff_accounts.sql',
'004_account_timezone.sql',
'004_xai_provider.sql',
'005_review_comments_confidence_score.sql',
'006_review_comment_grounding.sql',
'007_comment_feedback.sql',
'008_repo_config_min_severity.sql'
)
) THEN
RAISE NOTICE 'Pre-consolidation migrations already applied; skipping data backfills.';
RETURN;
END IF;

UPDATE file_reviews SET diff_input = NULL WHERE diff_input IS NOT NULL;

INSERT INTO llm_providers (name, api_format, base_url, enabled)
VALUES ('xAI', 'openai', 'https://api.x.ai/v1', FALSE)
ON CONFLICT (name) DO UPDATE SET
api_format = EXCLUDED.api_format,
base_url = EXCLUDED.base_url,
updated_at = now();
END
$backfill$;

UPDATE repo_configs
SET parsed_json = to_jsonb(
jsonb_set((parsed_json #>> '{}')::jsonb, '{review,min_severity}', '"P3"')::text
),
updated_at = now()
WHERE jsonb_typeof(parsed_json) = 'string'
AND (parsed_json #>> '{}')::jsonb->'review'->>'min_severity' = 'nit';

UPDATE repo_configs
SET parsed_json = jsonb_set(parsed_json, '{review,min_severity}', '"P3"'),
updated_at = now()
WHERE jsonb_typeof(parsed_json) = 'object'
AND parsed_json->'review'->>'min_severity' = 'nit';

INSERT INTO global_settings (key, value) VALUES ('review_max_files', '200')
ON CONFLICT (key) DO NOTHING;

UPDATE repo_configs
SET parsed_json = to_jsonb(
((parsed_json #>> '{}')::jsonb #- '{review,max_files}')::text
),
updated_at = now()
WHERE jsonb_typeof(parsed_json) = 'string'
AND (parsed_json #>> '{}')::jsonb->'review' ? 'max_files';

UPDATE repo_configs
SET parsed_json = parsed_json #- '{review,max_files}',
updated_at = now()
WHERE jsonb_typeof(parsed_json) = 'object'
AND parsed_json->'review' ? 'max_files';
7 changes: 7 additions & 0 deletions db/migrations/004_finding_instrumentation.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ALTER TABLE review_comments ADD COLUMN IF NOT EXISTS claim_type TEXT;
ALTER TABLE review_comments ADD COLUMN IF NOT EXISTS context_snippet TEXT COMPRESSION lz4;
ALTER TABLE review_comments ADD COLUMN IF NOT EXISTS disposition TEXT;

CREATE INDEX IF NOT EXISTS review_comments_claim_type_idx
ON review_comments (claim_type)
WHERE claim_type IS NOT NULL;
28 changes: 28 additions & 0 deletions db/migrations/005_gates_and_feedback.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
ALTER TABLE review_comments ADD COLUMN IF NOT EXISTS verify_reason TEXT COMPRESSION lz4;
ALTER TABLE file_reviews ADD COLUMN IF NOT EXISTS withheld_counts JSONB;

ALTER TABLE comment_feedback ADD COLUMN IF NOT EXISTS source TEXT NOT NULL DEFAULT 'github_webhook';
ALTER TABLE comment_feedback ALTER COLUMN github_comment_id DROP NOT NULL;
ALTER TABLE comment_feedback ADD COLUMN IF NOT EXISTS job_id UUID REFERENCES jobs(id) ON DELETE SET NULL;
ALTER TABLE comment_feedback ADD COLUMN IF NOT EXISTS labelled_by BIGINT;
ALTER TABLE comment_feedback ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT now();

CREATE UNIQUE INDEX IF NOT EXISTS comment_feedback_dashboard_unique_idx
ON comment_feedback (repository_id, fingerprint)
WHERE source = 'dashboard';

CREATE INDEX IF NOT EXISTS comment_feedback_repo_fingerprint_idx
ON comment_feedback (repository_id, fingerprint);

UPDATE repo_configs
SET parsed_json = to_jsonb(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Potentially destructive or lossy JSONB transformation

The migration performs a complex cast of JSONB to text and back to JSONB inside an UPDATE statement. Specifically, (parsed_json #>> '{}')::jsonb is used to treat the column as a JSONB object. If the existing parsed_json column contains a JSON scalar (like a string or number) rather than an object, the cast to jsonb will succeed, but the subsequent jsonb_set path operation might behave unexpectedly or fail depending on the structure. Furthermore, casting to text and back to JSONB can lose formatting or cause issues if the data contains non-standard characters. Given this is a schema migration, this logic is fragile.

Suggested change
SET parsed_json = to_jsonb(
Ensure the column is strictly JSONB and use native JSONB operators (e.g., jsonb_set(parsed_json, '{review,min_confidence}', '0'::jsonb, true)) instead of casting to text and back.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Potential data corruption in JSONB migration

The migration attempts to update a JSONB column by casting it to text, performing a jsonb_set, and then casting it back to jsonb. However, the use of (parsed_json #>> '{}')::jsonb combined with to_jsonb(...) and casting the result back to text suggests a misunderstanding of how PostgreSQL handles JSONB types. If parsed_json is already a JSONB column, wrapping it in to_jsonb and casting to text is redundant and potentially destructive to the underlying data structure or encoding.

Suggested change
SET parsed_json = to_jsonb(
Use jsonb_set directly on the column: SET parsed_json = jsonb_set(parsed_json, '{review,min_confidence}', '0'::jsonb, true)

jsonb_set(
(parsed_json #>> '{}')::jsonb,
'{review,min_confidence}',
'0'::jsonb,
true
)::text
)
WHERE parsed_json IS NOT NULL
AND ((parsed_json #>> '{}')::jsonb #>> '{review,min_confidence}') IS NOT NULL
AND ((parsed_json #>> '{}')::jsonb #>> '{review,min_confidence}')::numeric <> 0;
10 changes: 10 additions & 0 deletions db/migrations/006_recurrence_fingerprint.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ALTER TABLE review_comments ADD COLUMN IF NOT EXISTS fingerprint_v2 TEXT;
ALTER TABLE comment_feedback ADD COLUMN IF NOT EXISTS fingerprint_v2 TEXT;

CREATE INDEX IF NOT EXISTS review_comments_posted_fingerprint_v2_idx
ON review_comments (file_review_id, fingerprint_v2)
WHERE posted AND fingerprint_v2 IS NOT NULL;

CREATE INDEX IF NOT EXISTS comment_feedback_repo_fingerprint_v2_idx
ON comment_feedback (repository_id, fingerprint_v2)
WHERE fingerprint_v2 IS NOT NULL;
10 changes: 10 additions & 0 deletions db/migrations/007_withheld_counts_encoding.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- withheld_counts was written via JSON.stringify(), which postgres.js encodes as a jsonb STRING
-- scalar rather than an object. The TypeScript reader tolerated it, so the bug was invisible until a
-- SQL aggregate over the column returned zero for a review that had withheld five findings.
--
-- Normalize the existing rows so the column has one shape and SQL can read it. Idempotent: rows that
-- are already objects are not matched.
UPDATE file_reviews
SET withheld_counts = (withheld_counts #>> '{}')::jsonb
WHERE withheld_counts IS NOT NULL
AND jsonb_typeof(withheld_counts) = 'string';
33 changes: 33 additions & 0 deletions db/migrations/008_jsonb_encoding.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-- One jsonb encoding, everywhere.
--
-- Migration 007 fixed file_reviews.withheld_counts, which was written via JSON.stringify() bound to a
-- `$n::jsonb` placeholder -- postgres.js types that as json and stores a jsonb STRING SCALAR, so every
-- SQL JSON operator silently reads nothing. The same bug was live at five more write sites, because
-- `parseJsonColumn` tolerates both shapes and therefore nothing ever broke loudly:
--
-- repo_configs.parsed_json 11 rows -- forced 003 and 005 to branch around it
-- jobs.config_snapshot 204 rows
-- webhook_deliveries.payload 1,000 rows
-- repo_configs.fallback_models 0 rows -- all NULL today; write path was still wrong
-- repo_configs.size_overrides 0 rows
--
-- The writers now bind `$n::text::jsonb`, which is correct for objects AND arrays (binding the raw
-- value is not: db/client.ts normalizeParam turns a JS array into a Postgres array literal, which
-- casts straight back to a string scalar). This normalizes the rows those writers already produced.
--
-- Idempotent by construction: rows that are already objects or arrays fail the jsonb_typeof predicate.

UPDATE repo_configs SET parsed_json = (parsed_json #>> '{}')::jsonb
WHERE parsed_json IS NOT NULL AND jsonb_typeof(parsed_json) = 'string';

UPDATE repo_configs SET fallback_models = (fallback_models #>> '{}')::jsonb
WHERE fallback_models IS NOT NULL AND jsonb_typeof(fallback_models) = 'string';

UPDATE repo_configs SET size_overrides = (size_overrides #>> '{}')::jsonb
WHERE size_overrides IS NOT NULL AND jsonb_typeof(size_overrides) = 'string';

UPDATE jobs SET config_snapshot = (config_snapshot #>> '{}')::jsonb
WHERE config_snapshot IS NOT NULL AND jsonb_typeof(config_snapshot) = 'string';

UPDATE webhook_deliveries SET payload = (payload #>> '{}')::jsonb
WHERE payload IS NOT NULL AND jsonb_typeof(payload) = 'string';
18 changes: 18 additions & 0 deletions db/migrations/009_rule_channel.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- The deterministic rule channel: a second finding source alongside the LLM.
--
-- `source` defaults to 'llm', which backfills history correctly by construction -- every existing
-- row WAS model-generated -- so per-channel precision is computable over the whole corpus with no
-- data migration. Everything that counts findings must partition on it, or the numbers used to
-- judge the LLM channel silently include deterministic hits.
--
-- `rule_id` is the retirement signal. A rule with many generated and no posted findings is one the
-- verifier always rejects: delete it or fix it, rather than leaving it to add noise.

ALTER TABLE review_comments ADD COLUMN IF NOT EXISTS source TEXT NOT NULL DEFAULT 'llm';
ALTER TABLE review_comments ADD COLUMN IF NOT EXISTS rule_id TEXT;

-- Partial: rule findings are the rare case, and the queries that care are all "show me the non-LLM
-- ones". A full index would be almost entirely one repeated value.
CREATE INDEX IF NOT EXISTS review_comments_source_idx
ON review_comments (source)
WHERE source <> 'llm';
6 changes: 6 additions & 0 deletions db/migrations/009_vertex_api_format.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- Google Vertex AI is a distinct api_format from 'gemini': Vertex rejects plain API keys and
-- requires an OAuth2 Bearer token minted from a service-account JSON key, so it needs its own
-- adapter and its own row in the enum. Widen the CHECK constraint to allow it.
ALTER TABLE llm_providers DROP CONSTRAINT IF EXISTS llm_providers_api_format_check;
ALTER TABLE llm_providers ADD CONSTRAINT llm_providers_api_format_check
CHECK (api_format IN ('openai', 'anthropic', 'gemini', 'cloudflare-workers-ai', 'vertex'));
92 changes: 92 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import importX from 'eslint-plugin-import-x';
import reactHooks from 'eslint-plugin-react-hooks';
import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript';

export default tseslint.config(
{
ignores: [
'dist/**',
'node_modules/**',
// Recorded model responses and live re-run output: data, not source.
'test/fixtures/**',
'test/rerun/**',
// Generated by `wrangler types`.
'src/server/worker-env.d.ts',
'worker-configuration.d.ts',
],
},

js.configs.recommended,
...tseslint.configs.recommended,

{
files: ['**/*.{ts,tsx,js,mjs}'],
plugins: { 'import-x': importX, 'react-hooks': reactHooks },
settings: {
// The resolver has to understand the @server/@client/@shared aliases from tsconfig, or every
// internal import reads as unresolved and no-cycle/no-self-import are silently useless.
'import-x/resolver-next': [
createTypeScriptImportResolver({ project: './tsconfig.json' }),
],
},
rules: {
// TypeScript resolves every identifier already, and does it correctly for types, `declare`,
// and the Worker/DOM lib globals. Leaving this on means re-declaring hundreds of ambient
// globals in ESLint just to get a worse version of a check `npm run typecheck` already runs.
'no-undef': 'off',

// The base rule cannot see TypeScript's type-only positions; the TS one can.
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['error', {
// `catch {}` is the preferred form, but an unused binding is not worth an error.
caughtErrors: 'none',
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
}],

// `import-x/no-duplicates` and NOT the core `no-duplicate-imports`: the core rule is type-blind
// and flags the deliberate `import { Hono }` + `import type { Context }` split as a duplicate.
'import-x/no-duplicates': 'error',
'import-x/no-self-import': 'error',
'import-x/no-cycle': 'error',

// An error, not a warning: the four places whose dependency array is deliberately narrower
// than their closure now carry a line-level disable stating why. A new violation should fail.
'react-hooks/exhaustive-deps': 'error',

// Fires on the finding-title normalizer, which strips emoji and variation selectors from model
// output. Those combining characters are the point of it, and its behaviour is pinned by tests.
'no-misleading-character-class': 'off',

// `any` is used deliberately at the provider and DB boundaries, where the shape is genuinely
// unknown until it is parsed. Turning this on would mean ~100 suppressions, not better types.
'@typescript-eslint/no-explicit-any': 'off',
},
},

{
files: ['src/client/**/*.{ts,tsx}'],
rules: {
'react-hooks/rules-of-hooks': 'error',
},
},

{
// Plain-JS scripts are not covered by tsconfig, so they need their globals declared.
files: ['scripts/**/*.{js,mjs}'],
languageOptions: {
globals: {
console: 'readonly',
process: 'readonly',
Buffer: 'readonly',
fetch: 'readonly',
URL: 'readonly',
setTimeout: 'readonly',
clearTimeout: 'readonly',
__dirname: 'readonly',
},
},
},
);
Loading