Skip to content

feat(settings): add environments settings#1361

Merged
zerob13 merged 2 commits intodevfrom
codex/settings-project
Mar 18, 2026
Merged

feat(settings): add environments settings#1361
zerob13 merged 2 commits intodevfrom
codex/settings-project

Conversation

@zerob13
Copy link
Copy Markdown
Collaborator

@zerob13 zerob13 commented Mar 18, 2026

Summary by CodeRabbit

  • New Features
    • Environments page to browse/manage project directories used by sessions
    • Set/clear a default project directory (preselects on new chat)
    • Open project directories from Settings
    • Toggle visibility of temporary environments
    • Synthetic/default entry surfaced when default path is not in history
    • Shows session count and last-used timestamps per environment
  • Style
    • Compact inline header for Dashboard settings
  • Localization
    • Environments UI strings added across locales

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 18, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Adds a Settings → Environments feature: a derived SQLite new_environments table with migrations and sync logic, presenter IPC for environments and default project path, renderer UI and route, project store integration, extensive i18n, and tests for DB, presenters, and UI.

Changes

Cohort / File(s) Summary
Specs & Docs
docs/specs/settings-environments/plan.md, docs/specs/settings-environments/spec.md
Design and implementation plan + spec describing data model, migration v17/v18, sync strategy, UI behavior, and test plan.
New DB Table
src/main/presenter/sqlitePresenter/tables/newEnvironments.ts
Adds NewEnvironmentsTable: schema, v17/v18 migrations, list/rebuild/sync APIs, aggregation SQL, and clearing logic.
SQLite Presenter Integration
src/main/presenter/sqlitePresenter/index.ts
Wires NewEnvironmentsTable into lifecycle, migrations, init, clearNewAgentData, and ACP session ops to trigger path syncs.
Session/Import Sync
src/main/presenter/newAgentPresenter/sessionManager.ts, src/main/presenter/newAgentPresenter/legacyImportService.ts
Session CRUD now computes/Syncs affected environment paths; legacy import rebuilds environments (ACP workdir fallback) and tolerates rebuild errors.
Project Presenter & Types
src/main/presenter/projectPresenter/index.ts, src/shared/types/agent-interface.d.ts, src/shared/types/presenters/project.presenter.d.ts
Adds getEnvironments(), pathExists(), openDirectory(); introduces EnvironmentSummary type; temp-path detection and open/path helpers.
Config & Events
src/main/events.ts, src/main/presenter/configPresenter/index.ts, src/shared/types/presenters/legacy.presenters.d.ts
Adds CONFIG_EVENTS.DEFAULT_PROJECT_PATH_CHANGED and ConfigPresenter get/set for defaultProjectPath with IPC dispatch.
Renderer UI & Router
src/renderer/settings/components/EnvironmentsSettings.vue, src/renderer/settings/main.ts, src/renderer/settings/components/DashboardSettings.vue
New Environments settings component, route /environments, compact dashboard header tweak; UI actions (open, set/clear default), filtering, and sorting.
Renderer Store
src/renderer/src/stores/ui/project.ts
Adds environments state, defaultProjectPath handling, synthetic project reconciliation, selection-source tracking, fetch/set/clear default, openDirectory integration, and IPC listener.
Renderer Events & Init
src/renderer/src/events.ts, src/renderer/src/views/ChatTabView.vue
Renderer-side DEFAULT_PROJECT_PATH_CHANGED event and concurrent fetchProjects during init.
I18n
src/renderer/src/i18n/*/routes.json, src/renderer/src/i18n/*/settings.json
Adds "settings-environments" route labels and comprehensive "environments" translation blocks for 12 locales.
Tests — Main
test/main/presenter/sqlitePresenter.test.ts, test/main/presenter/sqlitePresenter/newEnvironmentsTable.test.ts, test/main/presenter/newAgentPresenter/*
Adds migration/backfill tests, NewEnvironmentsTable behavior tests, sessionManager/import tests asserting sync/rebuild calls.
Tests — Renderer
test/renderer/components/EnvironmentsSettings.test.ts, test/renderer/stores/projectStore.test.ts, test/renderer/components/NewThreadPage.test.ts
Adds EnvironmentsSettings unit tests, projectStore default-path/selection tests, and NewThreadPage preselected-project behavior tests.

Sequence Diagram(s)

sequenceDiagram
    participant User as Renderer (User)
    participant Store as Project Store
    participant Presenter as Project Presenter
    participant DB as SQLite (new_environments)
    rect rgba(200,200,255,0.5)
    User->>Store: fetchEnvironments()
    Store->>Presenter: getEnvironments()
    Presenter->>DB: newEnvironmentsTable.list()
    DB-->>Presenter: [EnvironmentRow...]
    Presenter->>Presenter: map rows -> EnvironmentSummary (fs.exists)
    Presenter-->>Store: [EnvironmentSummary...]
    Store-->>User: render environments
    end
Loading
sequenceDiagram
    participant SessionMgr as Session Manager
    participant EnvTable as NewEnvironmentsTable
    participant DB as SQLite
    rect rgba(200,255,200,0.5)
    SessionMgr->>EnvTable: syncPath(projectDir) on create/update/delete
    EnvTable->>DB: aggregate session counts & last_used_at
    DB-->>EnvTable: results
    EnvTable->>DB: upsert/delete rows in new_environments
    end
Loading
sequenceDiagram
    participant MainConfig as ConfigPresenter (Main)
    participant IPC as IPC/Event Bus
    participant Renderer as Renderer Process
    rect rgba(255,230,200,0.5)
    MainConfig->>MainConfig: setDefaultProjectPath(path)
    MainConfig->>IPC: emit DEFAULT_PROJECT_PATH_CHANGED
    IPC->>Renderer: CONFIG_EVENTS.DEFAULT_PROJECT_PATH_CHANGED
    Renderer->>Renderer: projectStore.loadDefaultProjectPath() / reconcile
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • deepinfect

Poem

🐰 Hop, hop, I traced each lane,

Paths and projects, kept in frame.
Sessions hummed, the table grew,
Defaults set — a rabbit’s cue.
Cheers for lists that find their way!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'feat(settings): add environments settings' accurately and directly describes the main objective: introducing a new Environments Settings feature to the application's settings module.

✏️ 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 codex/settings-project
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 175dc292dc

ℹ️ About Codex in GitHub

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

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +192 to +196
...projectStore.environments.filter(
(environment) =>
(!environment.isTemp || environment.path === defaultProjectPath.value) &&
(showMissing.value || environment.exists)
),
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 Badge Restore a way to show temp environments

This filter permanently excludes every isTemp entry unless it is already the default, and the component only defines showMissing—there is no companion state or control that can ever flip temp entries back on. As a result, temp workspaces never appear in this page, so users cannot review or reopen them even though this settings view is supposed to expose a collapsed temp section.

Useful? React with 👍 / 👎.

Comment on lines +29 to +33
SELECT
acp.conversation_id AS session_id,
acp.workdir AS path,
MAX(COALESCE(ns.updated_at, 0), COALESCE(acp.updated_at, 0)) AS activity_at
FROM acp_sessions acp
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 Badge Base ACP environment recency on session activity

For ACP-backed sessions without project_dir, last_used_at is derived from MAX(ns.updated_at, acp.updated_at). AcpSessionPersistence.saveSessionData() updates acp_sessions.updated_at whenever an ACP session is re-established, so merely reopening/rehydrating an ACP session will make that directory look "recent" even when no conversation activity happened. Because the settings page sorts by lastUsedAt, this causes environment history to drift away from actual chat usage.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
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: 3

🧹 Nitpick comments (7)
src/main/presenter/configPresenter/index.ts (1)

1877-1883: Skip redundant default-path broadcasts when value is unchanged.

On Line 1879, the setter writes and emits every time. Guarding unchanged values avoids unnecessary renderer churn.

♻️ Suggested tweak
   setDefaultProjectPath(projectPath: string | null): void {
     const normalized = projectPath?.trim() ? projectPath.trim() : null
+    const current = this.getDefaultProjectPath()
+    if (current === normalized) {
+      return
+    }
     this.setSetting('defaultProjectPath', normalized)
     eventBus.sendToRenderer(CONFIG_EVENTS.DEFAULT_PROJECT_PATH_CHANGED, SendTarget.ALL_WINDOWS, {
       path: normalized
     })
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/presenter/configPresenter/index.ts` around lines 1877 - 1883, The
setter setDefaultProjectPath currently always calls setSetting and broadcasts
CONFIG_EVENTS.DEFAULT_PROJECT_PATH_CHANGED; change it to first normalize the
incoming projectPath (as done now), read the existing value via
getSetting('defaultProjectPath') (or the appropriate accessor), compare the
existing value to the normalized value (treating null/empty equivalently), and
if they are equal simply return; only call this.setSetting('defaultProjectPath',
normalized) and
eventBus.sendToRenderer(CONFIG_EVENTS.DEFAULT_PROJECT_PATH_CHANGED,
SendTarget.ALL_WINDOWS, { path: normalized }) when the value actually changed.
src/main/presenter/newAgentPresenter/sessionManager.ts (1)

67-87: Consider syncing paths only when projectDir can change.

For updates that only change title/pin/draft, the extra path sync loop adds unnecessary DB work.

♻️ Suggested optimization
   update(
     id: string,
     fields: Partial<Pick<SessionRecord, 'title' | 'projectDir' | 'isPinned' | 'isDraft'>>
   ): void {
@@
     this.sqlitePresenter.newSessionsTable.update(id, dbFields)
 
+    if (fields.projectDir === undefined) {
+      return
+    }
+
     for (const path of this.sqlitePresenter.newEnvironmentsTable.listPathsForSession(id)) {
       affectedPaths.add(path)
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/presenter/newAgentPresenter/sessionManager.ts` around lines 67 - 87,
The code currently always gathers and syncs environment paths after calling
newSessionsTable.update, causing unnecessary work when only
title/isPinned/isDraft change; modify the logic to only compute affectedPaths
and call newEnvironmentsTable.syncPath when fields.projectDir is provided and
actually changes the session's project_dir value: fetch the current session row
(e.g., via this.sqlitePresenter.newSessionsTable.get(id) or equivalent), compare
its project_dir to fields.projectDir, and only if they differ run
newEnvironmentsTable.listPathsForSession(id) to build affectedPaths and call
newEnvironmentsTable.syncPath(path) for each path.
src/main/presenter/projectPresenter/index.ts (1)

43-53: Consider async existence checks for large environment lists.

fs.existsSync is synchronous and could block the event loop if the environments list grows large or if paths are on slow network drives. For typical usage this is likely fine, but worth noting.

♻️ Optional: Use async fs.promises.access for non-blocking checks
+import { promises as fsp } from 'fs'
+
 async getEnvironments(): Promise<EnvironmentSummary[]> {
   const rows = this.sqlitePresenter.newEnvironmentsTable.list()
-  return rows.map((row) => ({
-    path: row.path,
-    name: path.basename(row.path) || row.path,
-    sessionCount: row.session_count,
-    lastUsedAt: row.last_used_at,
-    isTemp: this.isTempPath(row.path),
-    exists: fs.existsSync(row.path)
-  }))
+  return Promise.all(
+    rows.map(async (row) => ({
+      path: row.path,
+      name: path.basename(row.path) || row.path,
+      sessionCount: row.session_count,
+      lastUsedAt: row.last_used_at,
+      isTemp: this.isTempPath(row.path),
+      exists: await fsp.access(row.path).then(() => true).catch(() => false)
+    }))
+  )
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/presenter/projectPresenter/index.ts` around lines 43 - 53,
getEnvironments currently uses the synchronous fs.existsSync which can block the
event loop for large lists; change it to perform async existence checks (e.g.,
use fs.promises.access or fs.promises.stat) and await them with Promise.all so
the method remains non-blocking. Specifically, update the getEnvironments method
in projectPresenter to map rows to async checks (retain fields: path, name via
path.basename, session_count, last_used_at, isTemp via isTempPath) and return
Promise<EnvironmentSummary[]> after awaiting all existence checks; ensure you
import/use fs.promises and preserve the existing property names (sessionCount,
lastUsedAt, isTemp, exists) in the returned objects.
test/main/presenter/sqlitePresenter/newEnvironmentsTable.test.ts (1)

58-78: Minor: Consider null-safe access for db in test bodies.

While beforeEach guarantees db is initialized, accessing db.prepare(...) directly without a null check (line 59, 62, etc.) relies on test ordering. The TypeScript type InstanceType<...> | null technically allows null.

This is a minor concern since the tests will always run after beforeEach, but adding a non-null assertion or guard could improve type safety.

💡 Option: Use non-null assertion in test body
   it('syncPath aggregates only non-draft sessions for a single directory', () => {
-    db.prepare(
+    db!.prepare(
       'INSERT INTO new_sessions (id, project_dir, is_draft, updated_at) VALUES (?, ?, ?, ?)'
     ).run('s1', '/work/app', 0, 100)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/main/presenter/sqlitePresenter/newEnvironmentsTable.test.ts` around
lines 58 - 78, The test accesses the possibly-null typed variable db directly in
the test body (calls to db.prepare(...)) even though beforeEach initializes it;
to fix, make the access null-safe by using a non-null assertion (e.g., db!) or
an explicit guard before using db, so update the calls in this test (and similar
tests) that invoke db.prepare(...) to use db! or check db !== null; this keeps
the existing logic (involving table.syncPath and table.list) but satisfies
TypeScript's InstanceType<...> | null typing and avoids nullable access
warnings.
src/main/presenter/sqlitePresenter/tables/newEnvironments.ts (2)

184-186: Consider removing syncForSession wrapper.

syncForSession simply delegates to syncSessionPaths without any additional logic. Unless this is intended as a future extension point, it adds unnecessary indirection.

💡 Option: Remove if not needed for API stability

If both methods need to exist for interface compliance or future extensibility, this is fine. Otherwise, consider consolidating to reduce maintenance surface.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/presenter/sqlitePresenter/tables/newEnvironments.ts` around lines
184 - 186, The method syncForSession is a redundant wrapper around
syncSessionPaths (it only calls this.syncSessionPaths(sessionId)); remove
syncForSession and update any callers to call syncSessionPaths(sessionId)
directly, or if it must remain for interface compatibility, add a comment noting
it’s intentionally passthrough; search for syncForSession usages and replace
them with syncSessionPaths to consolidate and delete the now-unnecessary
syncForSession function.

148-176: Minor: Redundant DISTINCT in outer query.

The inner UNION (without ALL) already removes duplicates between the two branches. The outer SELECT DISTINCT is redundant.

♻️ Remove redundant DISTINCT
       .prepare(
         `
-          SELECT DISTINCT path
+          SELECT path
           FROM (
             SELECT project_dir AS path
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/presenter/sqlitePresenter/tables/newEnvironments.ts` around lines
148 - 176, In listPathsForSession in newEnvironments.ts the outer SELECT
DISTINCT is redundant because the inner UNION already deduplicates; remove the
outer DISTINCT wrapper and change the query to directly select path from the
subquery (e.g., "SELECT path FROM ( ... )") or even better collapse to a single
UNION query without an extra outer SELECT, keeping the same parameter bindings
for sessionId in the call to .all(sessionId, sessionId) and preserving the
trimming/null checks in both branches.
src/main/presenter/sqlitePresenter/index.ts (1)

504-511: Variable path shadows imported path module.

The loop variable path (lines 506, 509, 510) shadows the imported path module from line 2. While this doesn't cause runtime issues in this context, it could lead to confusion during maintenance.

♻️ Rename loop variable to avoid shadowing
     const affectedPaths = new Set(this.newEnvironmentsTable.listPathsForSession(conversationId))
     await this.acpSessionsTable.upsert(conversationId, agentId, data)
-    for (const path of this.newEnvironmentsTable.listPathsForSession(conversationId)) {
-      affectedPaths.add(path)
+    for (const envPath of this.newEnvironmentsTable.listPathsForSession(conversationId)) {
+      affectedPaths.add(envPath)
     }
-    for (const path of affectedPaths) {
-      this.newEnvironmentsTable.syncPath(path)
+    for (const envPath of affectedPaths) {
+      this.newEnvironmentsTable.syncPath(envPath)
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/presenter/sqlitePresenter/index.ts` around lines 504 - 511, The loop
variable named "path" shadows the imported "path" module; update the loops that
iterate newEnvironmentsTable.listPathsForSession(conversationId) and the
affectedPaths iteration to use a non-conflicting name (e.g., envPath or
sessionPath) so the module import remains accessible and code is clearer; adjust
all references inside those loops (uses of path) and keep the rest of the logic
(calls to newEnvironmentsTable.syncPath and adding to affectedPaths / using
affectedPaths) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/presenter/newAgentPresenter/legacyImportService.ts`:
- Line 538: The call to
this.sqlitePresenter.newEnvironmentsTable.rebuildFromSessions() can throw after
the import commit and should not cause the whole import to be reported as
failed; wrap the call in a try/catch inside the import completion path (around
where rebuildFromSessions() is invoked), log the error (including the error
object/message) via the existing logger/presenter (e.g., processLogger or
this.logger) and do not rethrow—ensure the import status remains success even if
rebuildFromSessions() fails. Also add a short comment referencing
newEnvironmentsTable.rebuildFromSessions to explain why failures are non-fatal.

In `@src/renderer/settings/components/EnvironmentsSettings.vue`:
- Around line 179-188: The synthetic default environment object in
EnvironmentsSettings.vue is setting exists: true without verifying the
filesystem; update the code that creates the synthetic default (the object with
properties path, name, isSyntheticDefault) to determine existence by asking the
main process or existing utility (e.g., invoke an IPC like
ipcRenderer.invoke('pathExists', path) or call the app's path-existence helper)
and set exists accordingly, or if you intentionally accept the limitation, add a
concise comment next to isSyntheticDefault explaining that exists is
optimistically true and why; modify the creation site so the exists field
reflects the actual filesystem check or is documented as intentional.

In `@src/renderer/src/i18n/ru-RU/settings.json`:
- Around line 1400-1402: Update the Russian UI text for the environments
description (JSON key "description" in the environments section of
settings.json) to fix awkward phrasing "назначайте один каталогом по умолчанию
для новых чатов"; replace it with a grammatically correct phrase such as
"назначайте один каталог в качестве каталога по умолчанию для новых чатов" so
the sentence reads naturally in the UI.

---

Nitpick comments:
In `@src/main/presenter/configPresenter/index.ts`:
- Around line 1877-1883: The setter setDefaultProjectPath currently always calls
setSetting and broadcasts CONFIG_EVENTS.DEFAULT_PROJECT_PATH_CHANGED; change it
to first normalize the incoming projectPath (as done now), read the existing
value via getSetting('defaultProjectPath') (or the appropriate accessor),
compare the existing value to the normalized value (treating null/empty
equivalently), and if they are equal simply return; only call
this.setSetting('defaultProjectPath', normalized) and
eventBus.sendToRenderer(CONFIG_EVENTS.DEFAULT_PROJECT_PATH_CHANGED,
SendTarget.ALL_WINDOWS, { path: normalized }) when the value actually changed.

In `@src/main/presenter/newAgentPresenter/sessionManager.ts`:
- Around line 67-87: The code currently always gathers and syncs environment
paths after calling newSessionsTable.update, causing unnecessary work when only
title/isPinned/isDraft change; modify the logic to only compute affectedPaths
and call newEnvironmentsTable.syncPath when fields.projectDir is provided and
actually changes the session's project_dir value: fetch the current session row
(e.g., via this.sqlitePresenter.newSessionsTable.get(id) or equivalent), compare
its project_dir to fields.projectDir, and only if they differ run
newEnvironmentsTable.listPathsForSession(id) to build affectedPaths and call
newEnvironmentsTable.syncPath(path) for each path.

In `@src/main/presenter/projectPresenter/index.ts`:
- Around line 43-53: getEnvironments currently uses the synchronous
fs.existsSync which can block the event loop for large lists; change it to
perform async existence checks (e.g., use fs.promises.access or
fs.promises.stat) and await them with Promise.all so the method remains
non-blocking. Specifically, update the getEnvironments method in
projectPresenter to map rows to async checks (retain fields: path, name via
path.basename, session_count, last_used_at, isTemp via isTempPath) and return
Promise<EnvironmentSummary[]> after awaiting all existence checks; ensure you
import/use fs.promises and preserve the existing property names (sessionCount,
lastUsedAt, isTemp, exists) in the returned objects.

In `@src/main/presenter/sqlitePresenter/index.ts`:
- Around line 504-511: The loop variable named "path" shadows the imported
"path" module; update the loops that iterate
newEnvironmentsTable.listPathsForSession(conversationId) and the affectedPaths
iteration to use a non-conflicting name (e.g., envPath or sessionPath) so the
module import remains accessible and code is clearer; adjust all references
inside those loops (uses of path) and keep the rest of the logic (calls to
newEnvironmentsTable.syncPath and adding to affectedPaths / using affectedPaths)
unchanged.

In `@src/main/presenter/sqlitePresenter/tables/newEnvironments.ts`:
- Around line 184-186: The method syncForSession is a redundant wrapper around
syncSessionPaths (it only calls this.syncSessionPaths(sessionId)); remove
syncForSession and update any callers to call syncSessionPaths(sessionId)
directly, or if it must remain for interface compatibility, add a comment noting
it’s intentionally passthrough; search for syncForSession usages and replace
them with syncSessionPaths to consolidate and delete the now-unnecessary
syncForSession function.
- Around line 148-176: In listPathsForSession in newEnvironments.ts the outer
SELECT DISTINCT is redundant because the inner UNION already deduplicates;
remove the outer DISTINCT wrapper and change the query to directly select path
from the subquery (e.g., "SELECT path FROM ( ... )") or even better collapse to
a single UNION query without an extra outer SELECT, keeping the same parameter
bindings for sessionId in the call to .all(sessionId, sessionId) and preserving
the trimming/null checks in both branches.

In `@test/main/presenter/sqlitePresenter/newEnvironmentsTable.test.ts`:
- Around line 58-78: The test accesses the possibly-null typed variable db
directly in the test body (calls to db.prepare(...)) even though beforeEach
initializes it; to fix, make the access null-safe by using a non-null assertion
(e.g., db!) or an explicit guard before using db, so update the calls in this
test (and similar tests) that invoke db.prepare(...) to use db! or check db !==
null; this keeps the existing logic (involving table.syncPath and table.list)
but satisfies TypeScript's InstanceType<...> | null typing and avoids nullable
access warnings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c603b058-05ec-41cb-936e-8041c4b7c3ec

📥 Commits

Reviewing files that changed from the base of the PR and between dbbeb36 and 175dc29.

📒 Files selected for processing (51)
  • docs/specs/settings-environments/plan.md
  • docs/specs/settings-environments/spec.md
  • src/main/events.ts
  • src/main/presenter/configPresenter/index.ts
  • src/main/presenter/index.ts
  • src/main/presenter/newAgentPresenter/legacyImportService.ts
  • src/main/presenter/newAgentPresenter/sessionManager.ts
  • src/main/presenter/projectPresenter/index.ts
  • src/main/presenter/sqlitePresenter/index.ts
  • src/main/presenter/sqlitePresenter/tables/newEnvironments.ts
  • src/renderer/settings/components/DashboardSettings.vue
  • src/renderer/settings/components/EnvironmentsSettings.vue
  • src/renderer/settings/main.ts
  • src/renderer/src/events.ts
  • src/renderer/src/i18n/da-DK/routes.json
  • src/renderer/src/i18n/da-DK/settings.json
  • src/renderer/src/i18n/en-US/routes.json
  • src/renderer/src/i18n/en-US/settings.json
  • src/renderer/src/i18n/fa-IR/routes.json
  • src/renderer/src/i18n/fa-IR/settings.json
  • src/renderer/src/i18n/fr-FR/routes.json
  • src/renderer/src/i18n/fr-FR/settings.json
  • src/renderer/src/i18n/he-IL/routes.json
  • src/renderer/src/i18n/he-IL/settings.json
  • src/renderer/src/i18n/ja-JP/routes.json
  • src/renderer/src/i18n/ja-JP/settings.json
  • src/renderer/src/i18n/ko-KR/routes.json
  • src/renderer/src/i18n/ko-KR/settings.json
  • src/renderer/src/i18n/pt-BR/routes.json
  • src/renderer/src/i18n/pt-BR/settings.json
  • src/renderer/src/i18n/ru-RU/routes.json
  • src/renderer/src/i18n/ru-RU/settings.json
  • src/renderer/src/i18n/zh-CN/routes.json
  • src/renderer/src/i18n/zh-CN/settings.json
  • src/renderer/src/i18n/zh-HK/routes.json
  • src/renderer/src/i18n/zh-HK/settings.json
  • src/renderer/src/i18n/zh-TW/routes.json
  • src/renderer/src/i18n/zh-TW/settings.json
  • src/renderer/src/stores/ui/project.ts
  • src/renderer/src/views/ChatTabView.vue
  • src/shared/types/agent-interface.d.ts
  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/shared/types/presenters/project.presenter.d.ts
  • test/main/presenter/newAgentPresenter/legacyImportService.test.ts
  • test/main/presenter/newAgentPresenter/sessionManager.test.ts
  • test/main/presenter/projectPresenter/projectPresenter.test.ts
  • test/main/presenter/sqlitePresenter.test.ts
  • test/main/presenter/sqlitePresenter/newEnvironmentsTable.test.ts
  • test/renderer/components/EnvironmentsSettings.test.ts
  • test/renderer/components/NewThreadPage.test.ts
  • test/renderer/stores/projectStore.test.ts

@zerob13 zerob13 merged commit fe73eef into dev Mar 18, 2026
2 of 3 checks passed
@zerob13 zerob13 deleted the codex/settings-project branch March 29, 2026 05:41
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