Skip to content

feat(workspace): reuse checkout opens and trim repeated bootstrap - #125

Closed
Waishnav wants to merge 25 commits into
mainfrom
feat/reuse-chatgpt-workspaces
Closed

feat(workspace): reuse checkout opens and trim repeated bootstrap#125
Waishnav wants to merge 25 commits into
mainfrom
feat/reuse-chatgpt-workspaces

Conversation

@Waishnav

@Waishnav Waishnav commented Jul 31, 2026

Copy link
Copy Markdown
Owner

ChatGPT can issue open_workspace more than once for the same project during one conversation. Returning the full bootstrap every time repeats project instructions, nested instruction paths, skills, subagent metadata, and diagnostics in the model context.

DevSpace now treats workspace lifecycle and bootstrap delivery as separate concerns:

  • Checkout mode reuses the existing checkout workspace for the same canonical project path and ChatGPT conversation.
  • Every worktree-mode call creates a fresh managed worktree and a new workspace session, preserving the original isolation semantics.
  • The first open for a project in a ChatGPT conversation returns the full model bootstrap. Later checkout or worktree opens for that project omit the repeated bootstrap, even when a new worktree workspace is created.
  • Project-level bootstrap delivery and checkout bindings persist across MCP reconnects and DevSpace restarts.
  • Existing conversation bindings are backfilled into the new bootstrap ledger during migration.
  • The workspace card always receives the complete hidden display payload, so the UI remains fully populated while the model transcript stays compact.
  • Review checkpoints restore existing Git refs after restart, preventing pre-restart edits from disappearing from show_changes.

The implementation canonicalizes project paths, makes concurrent project-bootstrap claims atomic, validates restored checkout roots, and keeps worktree calls independent. Tests cover checkout reuse, fresh sequential and concurrent worktrees, both cross-mode directions, aliases, stale roots, persistence, checkpoint restoration, migration backfill, and UI display behavior.

Summary by CodeRabbit

  • New Features

    • Reopening a project in the same conversation now reuses its existing workspace.
    • New worktree opens create isolated workspaces with refreshed context.
    • Workspace cards now show mode, source location, worktree details, available providers, configured agents, and diagnostics.
    • Workspace setup information appears only when relevant.
  • Bug Fixes

    • Improved recovery of stale or unavailable workspaces.
    • Prevented duplicate workspace initialization during concurrent requests.
    • Preserved workspace and review state across reconnects and restarts.
    • Improved handling of unavailable review checkpoints.
  • Documentation

    • Updated guidance for workspace reuse and reopening behavior.
  • Tests

    • Expanded coverage for reuse, persistence, migration, concurrency, and recovery.

@greptile-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown

Greptile Summary

The PR separates workspace reuse from bootstrap delivery and preserves review state across repeated opens and restarts.

  • Reuses canonical checkout workspaces per ChatGPT conversation while keeping worktree opens isolated.
  • Persists checkout bindings and project bootstrap claims, including migration backfill.
  • Restores review checkpoint refs independently and falls back to workspace-open history when the last-shown baseline is unavailable.
  • Sends complete workspace metadata to the UI while omitting repeated bootstrap details from the model response.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/workspaces.ts Adds canonical project keys, persisted checkout reuse, stale-binding replacement, and fresh worktree behavior.
src/workspace-store.ts Adds atomic persisted conversation bindings and project-bootstrap claims.
src/review-checkpoints.ts Restores checkpoint refs after restart, tracks partial availability, and provides the public baseline fallback.
src/server.ts Connects conversation metadata to workspace opening, awaits checkpoint restoration, and separates model-visible bootstrap data from the complete card payload.
src/db/migrations.ts Adds binding and bootstrap-ledger migrations with backfill from historical conversation targets.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[open_workspace] --> B{Conversation scope available?}
  B -- No --> C[Create new workspace]
  B -- Yes --> D{Mode}
  D -- Worktree --> E[Create fresh managed worktree]
  D -- Checkout --> F{Valid persisted binding?}
  F -- Yes --> G[Reuse checkout workspace]
  F -- No --> H[Create checkout and persist binding]
  C --> I{First project open in conversation?}
  E --> I
  G --> I
  H --> I
  I -- Yes --> J[Return full bootstrap]
  I -- No --> K[Omit repeated model bootstrap]
  J --> L[Send complete hidden card payload]
  K --> L
  L --> M[Restore or initialize review checkpoints]
Loading

Reviews (11): Last reviewed commit: "fix(review): fall back when last-shown c..." | Re-trigger Greptile

Comment thread src/server.ts
Comment thread src/workspaces.ts Outdated
Comment thread src/workspaces.ts Outdated
@Waishnav
Waishnav force-pushed the feat/reuse-chatgpt-workspaces branch from 2e2c471 to 866d49e Compare July 31, 2026 09:06
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds conversation-scoped workspace reuse with persisted SQLite bindings and one-time bootstrap claims. It also adds concurrent checkpoint initialization, canonical path handling, conditional server output, richer workspace card metadata, migration coverage, and updated workflow documentation.

Changes

Conversation-scoped workspace reuse

Layer / File(s) Summary
Session scope handling
src/request-meta.ts, src/request-meta.test.ts, package.json
openAiConversationScopeId validates and returns openai/session metadata. The test script runs its tests.
Conversation binding persistence
src/db/schema.ts, src/db/migrations.ts, src/workspace-store.ts, src/oauth-store.test.ts
SQLite tables, migrations, backfill logic, store operations, and bootstrap claims persist conversation bindings and bootstrap records.
Workspace resolution and concurrency
src/workspaces.ts, src/workspaces.test.ts
Conversation-scoped checkout opens reuse valid canonical bindings, recover invalid workspaces, deduplicate concurrent opens, and create separate worktrees.
Checkpoint initialization
src/review-checkpoints.ts, src/review-checkpoints.test.ts
Workspace initialization reuses resolved state, waits for concurrent initialization, preserves valid references, and recreates missing references.
Open workspace output and display
src/server.ts, src/ui/card-types.ts, src/ui/workspace-app.tsx, docs/chatgpt-coding-workflow.md
Bootstrap fields are omitted after the first conversation open while cards retain complete metadata. Workspace payloads include worktree, agent, provider, and diagnostic details. It also documents checkout reuse and per-call worktree creation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

Poem

A rabbit tracks the session key,
And finds the workspace faithfully.
New worktrees keep their own domain,
Bootstrap speaks once, then rests again.
Cards show every detail bright,
SQLite guards the binding right.

Sequence Diagram(s)

sequenceDiagram
  participant ChatGPT
  participant Server
  participant WorkspaceRegistry
  participant WorkspaceStore
  participant SQLite
  ChatGPT->>Server: open_workspace(request metadata)
  Server->>WorkspaceRegistry: openWorkspace(target, conversationScopeId)
  WorkspaceRegistry->>WorkspaceStore: get or set conversation binding
  WorkspaceStore->>SQLite: query or persist binding
  SQLite-->>WorkspaceStore: workspace session binding
  WorkspaceRegistry->>WorkspaceStore: claimConversationBootstrap(scope, project)
  WorkspaceStore->>SQLite: insert or update bootstrap record
  WorkspaceRegistry-->>Server: workspace context and bootstrap flag
  Server-->>ChatGPT: conditional bootstrap output and workspace metadata
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: reusing checkout workspaces and reducing repeated bootstrap delivery.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/reuse-chatgpt-workspaces

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.

@Waishnav

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@Waishnav

Copy link
Copy Markdown
Owner Author

@greptileai

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server.ts (1)

755-772: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Add the reopen input that the PR objectives and the docs promise.

The PR objectives state that reopen: true requests a fresh workspace. The input schema at lines 756-772 accepts only path, mode, and baseRef. No reopen field exists, and OpenWorkspaceOptions in src/workspaces.ts carries only conversationScopeHash.

Without that input there is no way to force a new workspace inside one ChatGPT conversation. docs/chatgpt-coding-workflow.md line 34 still lists "the user explicitly asks to reopen" as a reopen trigger, but a repeated call now returns the existing workspaceId and omits bootstrap details. A user request to reopen therefore cannot be satisfied.

Add a reopen boolean to the input schema, forward it through OpenWorkspaceOptions, and bypass the binding lookup when it is set.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server.ts` around lines 755 - 772, Add an optional boolean reopen field
to the workspace tool input schema and OpenWorkspaceOptions, then propagate it
through the workspace-opening flow. Update the binding lookup logic to bypass
the existing workspace when reopen is true, while preserving normal reuse
behavior when it is false or omitted.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/workspaces.ts`:
- Around line 100-121: Make concurrent conversation-scoped opens deterministic
by updating openWorkspace in src/workspaces.ts#L100-L121 to register the
in-flight promise synchronously, keyed by conversationScopeHash, and compute
targetKey inside that promise; retain cleanup of the exact registered promise.
Update src/workspaces.test.ts#L194-L208 to assert order-independent results:
both calls share one workspace.id and workspace.root, and exactly one result has
includeBootstrapContext === true.

---

Outside diff comments:
In `@src/server.ts`:
- Around line 755-772: Add an optional boolean reopen field to the workspace
tool input schema and OpenWorkspaceOptions, then propagate it through the
workspace-opening flow. Update the binding lookup logic to bypass the existing
workspace when reopen is true, while preserving normal reuse behavior when it is
false or omitted.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e525385f-701f-49ca-a756-40126f1e95cc

📥 Commits

Reviewing files that changed from the base of the PR and between 8aa9d8b and 866d49e.

📒 Files selected for processing (13)
  • docs/chatgpt-coding-workflow.md
  • package.json
  • src/db/migrations.ts
  • src/db/schema.ts
  • src/oauth-store.test.ts
  • src/request-meta.test.ts
  • src/request-meta.ts
  • src/server.ts
  • src/ui/tool-display.test.ts
  • src/ui/tool-display.ts
  • src/workspace-store.ts
  • src/workspaces.test.ts
  • src/workspaces.ts

Comment thread src/workspaces.ts Outdated
Comment thread src/server.ts Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/workspace-store.ts (1)

144-172: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Avoid the extra round trip and the unchecked non-null assertion in setConversationBinding.

After .onConflictDoUpdate(...).run(), the method re-queries with this.getConversationBinding(...)!. This adds an extra database round trip. The ! only affects the type; it performs no runtime check. If getConversationBinding ever returns undefined at that point (for example, a delete of the binding races with this call), the method returns undefined typed as WorkspaceConversationBinding, and a caller can later dereference it and crash.

drizzle-orm supports .returning() after .onConflictDoUpdate() for SQLite, including composite-key targets. Use it to fetch the affected row from the same statement.

♻️ Proposed fix using `.returning()`
   setConversationBinding(input: {
     conversationScopeHash: string;
     targetKey: string;
     workspaceSessionId: string;
   }): WorkspaceConversationBinding {
     const now = new Date().toISOString();
-    this.database.db
+    const [row] = this.database.db
       .insert(workspaceConversationBindings)
       .values({
         conversationScopeHash: input.conversationScopeHash,
         targetKey: input.targetKey,
         workspaceSessionId: input.workspaceSessionId,
         createdAt: now,
         lastUsedAt: now,
       })
       .onConflictDoUpdate({
         target: [
           workspaceConversationBindings.conversationScopeHash,
           workspaceConversationBindings.targetKey,
         ],
         set: {
           workspaceSessionId: input.workspaceSessionId,
           lastUsedAt: now,
         },
       })
-      .run();
-
-    return this.getConversationBinding(input.conversationScopeHash, input.targetKey)!;
+      .returning()
+      .all();
+
+    return rowToWorkspaceConversationBinding(row);
   }

Please confirm .returning() behaves as expected with the better-sqlite3 driver at drizzle-orm 0.45.2 before applying.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/workspace-store.ts` around lines 144 - 172, Update setConversationBinding
to append Drizzle’s returning() to the insert/onConflictDoUpdate statement and
capture the affected row from run(), eliminating the follow-up
getConversationBinding query and its non-null assertion. Preserve the composite
conflict target and return the returned WorkspaceConversationBinding, with
explicit handling if the statement unexpectedly returns no row.
src/server.ts (1)

896-902: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Report skillDiagnostics as 0 for reused workspaces.

The other summary counts derive from the suppressed collections and become 0 on reuse. skillDiagnostics still reports workspace.skillDiagnostics.length, so the card can advertise diagnostics that the response omits.

♻️ Proposed summary alignment
-              skillDiagnostics: workspace.skillDiagnostics.length,
+              skillDiagnostics: includeBootstrapContext ? workspace.skillDiagnostics.length : 0,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server.ts` around lines 896 - 902, Update the summary construction around
the reused workspace handling so the skillDiagnostics count is reported as 0
when reused, matching the suppressed diagnostics collection in the response;
retain workspace.skillDiagnostics.length for non-reused workspaces.
src/workspaces.ts (1)

139-154: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Clean up the orphaned workspace session when a binding is discarded.

The recovery path removes the in-memory entry and the binding row. It leaves the persisted workspaceSessions row, so stale rows accumulate for every discarded binding. The catch block also hides the recovery reason, which makes allowed-root or filesystem problems hard to diagnose.

Consider deleting or marking the session record and logging the discarded binding at debug level. Do you want me to open an issue to track session cleanup for discarded bindings?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/workspaces.ts` around lines 139 - 154, Update the discarded-binding
recovery path in the workspace binding logic to also remove or mark deleted the
persisted workspace session associated with binding.workspaceSessionId,
alongside the existing in-memory and binding cleanup. Replace the empty catch in
this path with debug-level logging that includes the binding/session identifiers
and the failure details, while preserving recovery by discarding the unusable
binding.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/workspaces.test.ts`:
- Around line 194-208: Update the concurrent open assertions in the
persistentRegistry.openWorkspace test to avoid assuming which result has
includeBootstrapContext true. Assert that both results share the same workspace
identity and that exactly one result includes bootstrap context, while
preserving the existing shared-root and agent-file assertions.

---

Nitpick comments:
In `@src/server.ts`:
- Around line 896-902: Update the summary construction around the reused
workspace handling so the skillDiagnostics count is reported as 0 when reused,
matching the suppressed diagnostics collection in the response; retain
workspace.skillDiagnostics.length for non-reused workspaces.

In `@src/workspace-store.ts`:
- Around line 144-172: Update setConversationBinding to append Drizzle’s
returning() to the insert/onConflictDoUpdate statement and capture the affected
row from run(), eliminating the follow-up getConversationBinding query and its
non-null assertion. Preserve the composite conflict target and return the
returned WorkspaceConversationBinding, with explicit handling if the statement
unexpectedly returns no row.

In `@src/workspaces.ts`:
- Around line 139-154: Update the discarded-binding recovery path in the
workspace binding logic to also remove or mark deleted the persisted workspace
session associated with binding.workspaceSessionId, alongside the existing
in-memory and binding cleanup. Replace the empty catch in this path with
debug-level logging that includes the binding/session identifiers and the
failure details, while preserving recovery by discarding the unusable binding.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8d5a656d-0a46-4528-bd92-fe0506cbb741

📥 Commits

Reviewing files that changed from the base of the PR and between 8aa9d8b and 866d49e.

📒 Files selected for processing (13)
  • docs/chatgpt-coding-workflow.md
  • package.json
  • src/db/migrations.ts
  • src/db/schema.ts
  • src/oauth-store.test.ts
  • src/request-meta.test.ts
  • src/request-meta.ts
  • src/server.ts
  • src/ui/tool-display.test.ts
  • src/ui/tool-display.ts
  • src/workspace-store.ts
  • src/workspaces.test.ts
  • src/workspaces.ts

Comment thread src/workspaces.test.ts Outdated
@Waishnav

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@Waishnav

Copy link
Copy Markdown
Owner Author

@greptileai

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/ui/card-types.ts`:
- Around line 52-53: Update isExpandableCard to return true when either
agentProviders or agents is present and non-empty, alongside the existing
expandable-content checks. Add a regression test covering a card containing only
these metadata arrays and verify both fields expose the expansion affordance.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 45a10ac5-c984-44d4-a437-04754c3ff418

📥 Commits

Reviewing files that changed from the base of the PR and between 866d49e and ff9c337.

📒 Files selected for processing (6)
  • docs/chatgpt-coding-workflow.md
  • src/server.ts
  • src/ui/card-types.ts
  • src/ui/tool-display.test.ts
  • src/workspaces.test.ts
  • src/workspaces.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/chatgpt-coding-workflow.md
  • src/server.ts
  • src/workspaces.ts

Comment thread src/ui/card-types.ts Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/workspace-store.ts (1)

144-172: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Use .returning() to avoid a redundant query and a non-null assertion.

setConversationBinding performs insert().onConflictDoUpdate() and then issues a separate getConversationBinding call with a non-null assertion (!) to obtain the result. Drizzle's SQLite dialect supports .returning() directly after .onConflictDoUpdate(), including with a composite conflict target. Use it to return the upserted row from the same statement, removing the extra round-trip and the assumption that the row still exists when the second query runs.

♻️ Proposed refactor using `.returning()`
   setConversationBinding(input: {
     conversationScopeHash: string;
     targetKey: string;
     workspaceSessionId: string;
   }): WorkspaceConversationBinding {
     const now = new Date().toISOString();
-    this.database.db
+    const [row] = this.database.db
       .insert(workspaceConversationBindings)
       .values({
         conversationScopeHash: input.conversationScopeHash,
         targetKey: input.targetKey,
         workspaceSessionId: input.workspaceSessionId,
         createdAt: now,
         lastUsedAt: now,
       })
       .onConflictDoUpdate({
         target: [
           workspaceConversationBindings.conversationScopeHash,
           workspaceConversationBindings.targetKey,
         ],
         set: {
           workspaceSessionId: input.workspaceSessionId,
           lastUsedAt: now,
         },
       })
-      .run();
-
-    return this.getConversationBinding(input.conversationScopeHash, input.targetKey)!;
+      .returning()
+      .all();
+
+    return rowToWorkspaceConversationBinding(row);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/workspace-store.ts` around lines 144 - 172, Update setConversationBinding
to chain returning() after onConflictDoUpdate(), capture the single returned
workspace conversation binding, and return it directly. Remove the separate
getConversationBinding call and its non-null assertion while preserving the
existing insert values and composite conflict target.
src/request-meta.ts (1)

11-20: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Consider a keyed hash for real anonymization.

openAiConversationScopeHash uses a plain, unkeyed SHA-256 hash. The "openai" prefix is a public constant, so it adds no secrecy. Anyone with a candidate session string can recompute the same hash and confirm a match against the persisted conversationScopeHash column.

Use HMAC-SHA256 with a server-local secret instead of a plain hash. This prevents offline correlation of persisted conversation scopes if the database is ever exposed, and keeps hash determinism per install.

Confirm whether openai/session values are guaranteed to be high-entropy, opaque identifiers. If they are predictable or low-entropy, the current unkeyed hash provides weaker protection than the "anonymized" claim implies.

🔒 Proposed HMAC-based fix
-import { createHash } from "node:crypto";
+import { createHmac } from "node:crypto";
+
+const CONVERSATION_SCOPE_SECRET =
+  process.env.DEVSPACE_CONVERSATION_SCOPE_SECRET ?? "devspace-conversation-scope";

 export function openAiConversationScopeHash(
   meta: Record<string, unknown> | undefined,
 ): string | undefined {
   const session = metadataString(meta, "openai/session");
   if (!session) return undefined;

-  return createHash("sha256")
-    .update(JSON.stringify(["openai", session]))
-    .digest("hex");
+  return createHmac("sha256", CONVERSATION_SCOPE_SECRET)
+    .update(JSON.stringify(["openai", session]))
+    .digest("hex");
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/request-meta.ts` around lines 11 - 20, Update openAiConversationScopeHash
to use deterministic HMAC-SHA256 with a server-local secret instead of the
unkeyed createHash flow, preserving undefined behavior when openai/session is
absent. Reuse the project’s established secret/configuration source and confirm
whether session values are opaque high-entropy identifiers; ensure the resulting
digest remains stable per installation.
src/ui/tool-display.test.ts (1)

28-31: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

These reuse cases do not exercise any reuse behavior.

src/server.ts Lines 910-918 build the card summary from mode, agentsFiles, availableAgentsFiles, skills, agentProviders, agents, and skillDiagnostics. It never sets reused. getToolDisplay ignores summary entirely, and getToolHeaderSummary reads only mode, agentsFiles, and skills. Both assertions therefore pass for a key that no producer emits, so they pin no reuse contract.

Choose one option:

  • Add reused to the card summary in src/server.ts and to ToolHeaderSummary handling if the widget should show reuse.
  • Remove reused from these test cards to keep the fixtures aligned with the emitted summary.

Also applies to: 102-108

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/tool-display.test.ts` around lines 28 - 31, Align the reuse test
fixtures with the actual card-summary contract: either add and propagate reused
through the server card summary and ToolHeaderSummary/getToolDisplay handling if
reuse should affect the widget, or remove summary.reused from both assertions if
it is not emitted. Keep the chosen behavior consistent across the affected tests
and production symbols.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server.ts`:
- Around line 811-816: Update the workspace setup around
reviewCheckpoints.initializeWorkspace so persisted workspaces are initialized
whenever reviewChanges/show_changes may access them, including when
includeBootstrapContext is false. Register or initialize the workspace before
the config.widgets === "changes" and includeBootstrapContext gate, while
preserving the existing initialization inputs and avoiding duplicate
initialization.

In `@src/workspaces.test.ts`:
- Around line 209-210: Update the concurrent open assertions in the workspace
test to avoid assuming which Promise.all result receives bootstrap context.
Assert that both results share the same workspace identity and that exactly one
concurrent open includes bootstrap context, preserving the existing behavior
without checking a fixed result order.

---

Nitpick comments:
In `@src/request-meta.ts`:
- Around line 11-20: Update openAiConversationScopeHash to use deterministic
HMAC-SHA256 with a server-local secret instead of the unkeyed createHash flow,
preserving undefined behavior when openai/session is absent. Reuse the project’s
established secret/configuration source and confirm whether session values are
opaque high-entropy identifiers; ensure the resulting digest remains stable per
installation.

In `@src/ui/tool-display.test.ts`:
- Around line 28-31: Align the reuse test fixtures with the actual card-summary
contract: either add and propagate reused through the server card summary and
ToolHeaderSummary/getToolDisplay handling if reuse should affect the widget, or
remove summary.reused from both assertions if it is not emitted. Keep the chosen
behavior consistent across the affected tests and production symbols.

In `@src/workspace-store.ts`:
- Around line 144-172: Update setConversationBinding to chain returning() after
onConflictDoUpdate(), capture the single returned workspace conversation
binding, and return it directly. Remove the separate getConversationBinding call
and its non-null assertion while preserving the existing insert values and
composite conflict target.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ac3719ff-95f2-4b9e-81c2-7f8d8846f21b

📥 Commits

Reviewing files that changed from the base of the PR and between 8aa9d8b and ff9c337.

📒 Files selected for processing (13)
  • docs/chatgpt-coding-workflow.md
  • package.json
  • src/db/migrations.ts
  • src/db/schema.ts
  • src/oauth-store.test.ts
  • src/request-meta.test.ts
  • src/request-meta.ts
  • src/server.ts
  • src/ui/card-types.ts
  • src/ui/tool-display.test.ts
  • src/workspace-store.ts
  • src/workspaces.test.ts
  • src/workspaces.ts

Comment thread src/server.ts Outdated
Comment thread src/workspaces.test.ts Outdated
@Waishnav

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@Waishnav

Copy link
Copy Markdown
Owner Author

@greptileai

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/review-checkpoints.test.ts (1)

43-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extend the restart test to also cover workspace_open preservation.

This test confirms that since: "last_shown" (baselineRef) survives a manager restart. initializeWorkspaceState in src/review-checkpoints.ts (lines 137-149) independently preserves openRef and baselineRef based on separate hasCommitRef checks. Add an assertion for since: "workspace_open" after restart, so the test verifies both refs survive restart, not only the default one.

🧪 Suggested addition
   assert.equal(afterRestart.summary.files, 2);
   assert.match(afterRestart.patch, /world/);
+
+  const sinceOpen = await restartedManager.reviewChanges({
+    workspaceId: "ws_review",
+    root,
+    since: "workspace_open",
+    markReviewed: false,
+  });
+  assert.equal(sinceOpen.summary.files, 2);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/review-checkpoints.test.ts` around lines 43 - 52, Extend the restart
scenario in the review checkpoint test after the existing restartedManager
review to call reviewChanges with since: "workspace_open" and markReviewed:
false, then assert the result matches the expected workspace-open baseline
(including the appropriate file count or patch content). Keep the existing
last_shown assertions unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/review-checkpoints.ts`:
- Around line 120-163: Update the reviewChanges state lookup to treat a cached
WorkspaceReviewState as ready only when it has either gitRoot or diagnostic;
when both are absent, re-enter initializeWorkspace so the existing
initialization promise is awaited instead of rejecting prematurely. Preserve the
existing deduplication behavior for concurrent workspace initialization and use
the relevant state/cache symbols already in the reviewChanges flow.

---

Nitpick comments:
In `@src/review-checkpoints.test.ts`:
- Around line 43-52: Extend the restart scenario in the review checkpoint test
after the existing restartedManager review to call reviewChanges with since:
"workspace_open" and markReviewed: false, then assert the result matches the
expected workspace-open baseline (including the appropriate file count or patch
content). Keep the existing last_shown assertions unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b4d0541-f32c-4dc9-aeae-39ff81fcfce1

📥 Commits

Reviewing files that changed from the base of the PR and between ff9c337 and 8ea171f.

📒 Files selected for processing (3)
  • src/review-checkpoints.test.ts
  • src/review-checkpoints.ts
  • src/server.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/server.ts

Comment thread src/review-checkpoints.ts
Comment thread src/review-checkpoints.ts Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/review-checkpoints.ts (1)

120-134: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Publish the workspace state only after initialization resolves.

Line 127 inserts an incomplete state into the shared states map before getGitEligibility runs. reviewChanges treats any present state as ready: it checks if (!state) at line 82, so it skips initializeWorkspace, then fails at line 87 on !state?.gitRoot. A show_changes call that overlaps an in-flight open_workspace initialization therefore throws "show_changes requires a Git workspace in this version." even though initialization is about to succeed.

The previous fix hardened the initializeWorkspace fast path, but the early publication at line 127 is the root cause and reviewChanges still reads the map directly. Build the state locally and insert it once, after gitRoot or diagnostic is set.

🛡️ Proposed fix to remove the incomplete-state window
   const refs = reviewRefs(workspaceId);
   const state: WorkspaceReviewState = { root, ...refs };
-  states.set(workspaceId, state);
 
   try {
     const eligibility = await getGitEligibility(root);
     if (!eligibility.ok || !eligibility.gitRoot) {
       state.diagnostic = eligibility.message ?? "show_changes requires a Git workspace in this version.";
       return;
     }
@@
   } catch (error) {
     state.diagnostic = error instanceof Error ? error.message : String(error);
+  } finally {
+    states.set(workspaceId, state);
   }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/review-checkpoints.ts` around lines 120 - 134, Update
initializeWorkspaceState so the WorkspaceReviewState remains local while
getGitEligibility and the subsequent initialization complete; move
states.set(workspaceId, state) to the point after either gitRoot or diagnostic
has been assigned. Preserve the existing initialized state contents and
diagnostic behavior, ensuring reviewChanges never observes an incomplete state.
🧹 Nitpick comments (1)
src/workspace-store.ts (1)

144-202: 🗄️ Data Integrity & Integration | 🔵 Trivial

Add retention for unused workspace_conversation_bindings.

workspace_conversation_bindings accumulates one row per conversation/target unless the same binding is reused or the linked workspace_session is deleted. last_used_at is updated, but no query or prune deletes stale binding rows, and no index covers it. Add a last_used_at-based cleanup path plus an index if this table is expected to grow long-term.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/workspace-store.ts` around lines 144 - 202, The workspace conversation
binding store updates lastUsedAt but never removes stale rows or indexes that
timestamp. Add a lastUsedAt index to the workspaceConversationBindings schema
and implement a cleanup method that deletes bindings older than a supplied
retention cutoff, preserving active bindings; expose the cleanup through the
store’s existing maintenance path if one exists.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/workspaces.ts`:
- Around line 137-166: Limit the try/catch in openConversationWorkspace to
validating the persisted binding: resolving the workspace, checking
workspace.root with stat, and determining whether it is a directory. Move
reusedWorkspaceContext(workspace) outside the try so errors from context
initialization, including agent/profile loading, propagate without deleting the
healthy binding or creating a replacement workspace. Preserve
touchConversationBinding and the existing cleanup behavior only when binding
validation fails.
- Around line 179-190: The reusedWorkspaceContext method should skip
loadInitialAgentsFiles and findAvailableAgentsFiles when includeBootstrapContext
is false, while preserving agentProfiles loading. Return empty agentsFiles and
availableAgentsFiles arrays for reused contexts, and update the reboundWorkspace
and concurrent worktree reuse assertions in the workspace tests to expect the
empty results.

---

Duplicate comments:
In `@src/review-checkpoints.ts`:
- Around line 120-134: Update initializeWorkspaceState so the
WorkspaceReviewState remains local while getGitEligibility and the subsequent
initialization complete; move states.set(workspaceId, state) to the point after
either gitRoot or diagnostic has been assigned. Preserve the existing
initialized state contents and diagnostic behavior, ensuring reviewChanges never
observes an incomplete state.

---

Nitpick comments:
In `@src/workspace-store.ts`:
- Around line 144-202: The workspace conversation binding store updates
lastUsedAt but never removes stale rows or indexes that timestamp. Add a
lastUsedAt index to the workspaceConversationBindings schema and implement a
cleanup method that deletes bindings older than a supplied retention cutoff,
preserving active bindings; expose the cleanup through the store’s existing
maintenance path if one exists.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5d2ba1a0-3114-4e2f-b8e1-ce666cadf55f

📥 Commits

Reviewing files that changed from the base of the PR and between 8aa9d8b and f7fc11b.

📒 Files selected for processing (16)
  • docs/chatgpt-coding-workflow.md
  • package.json
  • src/db/migrations.ts
  • src/db/schema.ts
  • src/oauth-store.test.ts
  • src/request-meta.test.ts
  • src/request-meta.ts
  • src/review-checkpoints.test.ts
  • src/review-checkpoints.ts
  • src/server.ts
  • src/ui/card-types.test.ts
  • src/ui/card-types.ts
  • src/ui/workspace-app.tsx
  • src/workspace-store.ts
  • src/workspaces.test.ts
  • src/workspaces.ts

Comment thread src/workspaces.ts Outdated
Comment thread src/workspaces.ts
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

@Waishnav: I will perform a full review of the final ae54601 changes. I will check the workspace contract, persistence and migration behavior, concurrency handling, and regression coverage.

✅ Action performed

Full review finished.

@Waishnav

Waishnav commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

[GPT-5.6 Thinking] RESPONDING ON BEHALF OF WAISHNAV

Merged current origin/main into the branch without rewriting history. The only conflict was the repository guidance file; the newer main version was retained. Final head is 3597e2e, is mergeable, and the full test suite, typecheck, and production build pass after the merge.

@coderabbitai full review
@greptileai

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

@Waishnav: I will perform a full review of the merged branch at 3597e2e. I will check the merge impact, workspace lifecycle behavior, persistence, migrations, concurrency, and regression coverage.

✅ Action performed

Full review finished.

@Waishnav

Waishnav commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

[GPT-5.6 Thinking] RESPONDING ON BEHALF OF WAISHNAV

Follow-up CI fix in 7942f2b: the binding-preservation regression now looks up the canonical project key via realpath(root). The previous macOS failure was a test-only /var versus /private/var mismatch; production canonicalization was correct. Focused workspace tests and typecheck pass.

@coderabbitai full review
@greptileai

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

@Waishnav: I will perform a full review of the follow-up CI fix in 7942f2b. I will check the canonical-path test correction and its effect on workspace binding behavior.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/review-checkpoints.test.ts (1)

82-82: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert that the baseline ref exists before deleting it.

git update-ref -d exits successfully when the ref is absent. Line 82 hardcodes the ref name, while the production code derives it through safeWorkspaceRefSegment. If that naming changes, this line deletes nothing, the partial-restore branch is never exercised, and the test still passes.

Verify the ref resolves before the deletion.

💚 Proposed hardening
+  await git(root, ["rev-parse", "--verify", "refs/devspace/review/ws_review/baseline^{commit}"]);
   await git(root, ["update-ref", "-d", "refs/devspace/review/ws_review/baseline"]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/review-checkpoints.test.ts` at line 82, Update the deletion setup in the
review-checkpoint test to derive the baseline ref using the same
safeWorkspaceRefSegment-based naming as production, then verify that ref
resolves before invoking git update-ref -d. Make the test fail explicitly when
the expected baseline ref is absent so the partial-restore branch is genuinely
exercised.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/chatgpt-coding-workflow.md`:
- Around line 36-40: Add checkout/worktree mode changes to the reopen conditions
in the open_workspace guidance, alongside unknown workspaceId, project-folder
changes, and requests for a new isolated worktree. Keep the existing conditions
unchanged and ensure the documented behavior matches the corresponding rules in
src/server.ts and AGENTS.md.

In `@src/request-meta.ts`:
- Around line 1-13: Update openAiConversationScopeId and the reusable
conversation-binding flow so the client-provided _meta value alone cannot
determine workspace reuse; accept openai/session only when verified against an
explicitly trusted ChatGPT identity token/context, or incorporate authenticated
client identity into the binding key. Preserve reuse only for requests sharing
the same authenticated identity and validated session scope.

In `@src/workspaces.ts`:
- Around line 172-178: Update the reusable workspace branch in the
workspace-opening method to await reusedWorkspaceContext before calling
store?.claimConversationBootstrap. Apply the claimed boolean to the constructed
context result’s includeBootstrapContext field, preserving
touchConversationBinding and existing fallback behavior.

---

Nitpick comments:
In `@src/review-checkpoints.test.ts`:
- Line 82: Update the deletion setup in the review-checkpoint test to derive the
baseline ref using the same safeWorkspaceRefSegment-based naming as production,
then verify that ref resolves before invoking git update-ref -d. Make the test
fail explicitly when the expected baseline ref is absent so the partial-restore
branch is genuinely exercised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7b2fc268-7ad5-4b6e-8e9b-c1435466c9e7

📥 Commits

Reviewing files that changed from the base of the PR and between 8aa9d8b and ae54601.

📒 Files selected for processing (17)
  • AGENTS.md
  • docs/chatgpt-coding-workflow.md
  • package.json
  • src/db/migrations.ts
  • src/db/schema.ts
  • src/oauth-store.test.ts
  • src/request-meta.test.ts
  • src/request-meta.ts
  • src/review-checkpoints.test.ts
  • src/review-checkpoints.ts
  • src/server.ts
  • src/ui/card-types.test.ts
  • src/ui/card-types.ts
  • src/ui/workspace-app.tsx
  • src/workspace-store.ts
  • src/workspaces.test.ts
  • src/workspaces.ts

Comment thread docs/chatgpt-coding-workflow.md
Comment thread src/request-meta.ts
Comment thread src/workspaces.ts
Comment thread src/review-checkpoints.ts Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/workspaces.ts (1)

172-178: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Claim the bootstrap after the reused context is built.

Line 176 evaluates claimConversationBootstrap before reusedWorkspaceContext runs its body. If loadLocalAgentProfiles, loadInitialAgentsFiles, or findAvailableAgentsFiles throws, the claim is already consumed. The call rejects, and every later open for the same conversationScopeId and projectKey receives includeBootstrapContext: false. The bootstrap payload is then never delivered for that project in that conversation.

src/workspaces.test.ts lines 183-202 reach this failure path. That test asserts the binding survives, but it does not assert that the bootstrap claim survives.

Build the context first, then claim, then attach the flag.

The new-workspace branch at lines 190-194 is already safe, because nothing after the claim can throw.

🐛 Proposed fix for the claim ordering
       if (reusableWorkspace) {
         this.store?.touchConversationBinding(conversationScopeId, targetKey);
-        return await this.reusedWorkspaceContext(
-          reusableWorkspace,
-          this.store?.claimConversationBootstrap(conversationScopeId, projectKey) ?? true,
-        );
+        const reused = await this.reusedWorkspaceContext(reusableWorkspace, true);
+        return {
+          ...reused,
+          includeBootstrapContext:
+            this.store?.claimConversationBootstrap(conversationScopeId, projectKey) ?? true,
+        };
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/workspaces.ts` around lines 172 - 178, Update the reusable-workspace
branch in the workspace-opening flow to build the result with
reusedWorkspaceContext before calling claimConversationBootstrap, so failures
during context construction do not consume the claim. After the context is
built, claim using conversationScopeId and projectKey, then attach the resulting
includeBootstrapContext flag to the returned context; preserve
touchConversationBinding and the new-workspace branch behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/chatgpt-coding-workflow.md`:
- Around line 20-23: Update the session-scope retention statement in the ChatGPT
workflow documentation to say that DevSpace persists a derived hash of the
opaque openai/session metadata, not the raw session identifier. Keep the
correlation-scope and workspaceId reuse behavior unchanged.

In `@src/review-checkpoints.ts`:
- Around line 55-60: Update the existing-state readiness checks in
initializeWorkspace and the corresponding reviewChanges flow to treat only a
defined existingState.gitRoot as initialized; do not use
existingState.diagnostic to short-circuit eligibility retries. Preserve the
diagnostic as the latest failure, and add a regression covering an unborn
repository that receives its first commit before reviewChanges is called again
with the same manager.

---

Duplicate comments:
In `@src/workspaces.ts`:
- Around line 172-178: Update the reusable-workspace branch in the
workspace-opening flow to build the result with reusedWorkspaceContext before
calling claimConversationBootstrap, so failures during context construction do
not consume the claim. After the context is built, claim using
conversationScopeId and projectKey, then attach the resulting
includeBootstrapContext flag to the returned context; preserve
touchConversationBinding and the new-workspace branch behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 492ecee9-d739-4c18-b6a5-05b1fd8dbb5f

📥 Commits

Reviewing files that changed from the base of the PR and between 5f677b8 and 7942f2b.

📒 Files selected for processing (16)
  • docs/chatgpt-coding-workflow.md
  • package.json
  • src/db/migrations.ts
  • src/db/schema.ts
  • src/oauth-store.test.ts
  • src/request-meta.test.ts
  • src/request-meta.ts
  • src/review-checkpoints.test.ts
  • src/review-checkpoints.ts
  • src/server.ts
  • src/ui/card-types.test.ts
  • src/ui/card-types.ts
  • src/ui/workspace-app.tsx
  • src/workspace-store.ts
  • src/workspaces.test.ts
  • src/workspaces.ts

Comment thread docs/chatgpt-coding-workflow.md
Comment thread src/review-checkpoints.ts
@Waishnav

Waishnav commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

[GPT-5.6 Thinking] RESPONDING ON BEHALF OF WAISHNAV

Pushed the final review follow-ups on head ffb83de as separate commits:

  • 7105d1d: claim project bootstrap only after reused context loading succeeds, with failed-first-open recovery coverage.
  • 95b08f2: preserve workspace-open and last-shown checkpoint meanings independently after partial ref loss.
  • 13e9fef: document checkout/worktree mode switches as a new-open condition.
  • ffb83de: mark a deliberately recreated baseline available after a marked workspace-open review.

I did not adopt the request to transform openai/session: within this owner-authenticated local server it is an opaque equality key, not an authorization credential, and hashing/HMAC does not prevent a client from submitting the same key. A principal-bound scope would require a separate multi-principal authorization model.

Verification on ffb83de: full npm test, npm run typecheck, npm run build, and git diff --check pass locally.

@coderabbitai full review
@greptileai

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

@Waishnav: I will perform a full review of head ffb83de. I will check the final follow-ups, workspace lifecycle behavior, checkpoint recovery, persistence, and regression coverage.

✅ Action performed

Full review finished.

@Waishnav

Waishnav commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

[GPT-5.6 Thinking] RESPONDING ON BEHALF OF WAISHNAV

Final follow-up pushed on head 598c737: review-checkpoint eligibility failures are now retryable after repository state changes. An unborn repository can receive its first commit and recover show_changes without restarting DevSpace. Added focused regression coverage; full tests, typecheck, production build, and diff checks pass.

The review comment describing a persisted derived hash is obsolete against the final branch: b38e46c removed hashing and the schema now stores conversation_scope_id directly.

@coderabbitai full review
@greptileai

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

@Waishnav I will perform a full review of commit 598c737. I will verify retryable review-checkpoint initialization, the new regression coverage, and direct conversation_scope_id storage.


✏️ Learnings added
Learnt from: Waishnav
URL: https://github.com/Waishnav/devspace/pull/125

Timestamp: 2026-08-04T09:45:02.331Z
Learning: In PR `#125`, `conversation_scope_id` stores the opaque OpenAI conversation scope ID directly. Commit `b38e46c` removed the prior derived-hash implementation, so reviews must not report persisted hashing for this value.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai 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.

♻️ Duplicate comments (1)
src/review-checkpoints.ts (1)

58-63: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Readiness includes diagnostic, so eligibility never retries. initializeWorkspace and isInitializedState both accept a defined diagnostic as initialized. A workspace that fails eligibility once keeps returning that diagnostic for the lifetime of the manager, even after the repository becomes eligible.

  • src/review-checkpoints.ts#L58-L63: drop existingState.diagnostic !== undefined from the early return, and change isInitializedState at Lines 178-180 to test only gitRoot.
  • src/review-checkpoints.test.ts#L116-L138: add a case that initializes a repository without HEAD, creates the first commit, and then calls reviewChanges on the same manager.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/review-checkpoints.ts` around lines 58 - 63, Update
src/review-checkpoints.ts: in the existing-state early return around
initializeWorkspace, stop treating existingState.diagnostic as initialized, and
change isInitializedState to check only gitRoot so failed eligibility can be
retried. Add a test in src/review-checkpoints.test.ts covering initialization
without HEAD, creating the first commit, then calling reviewChanges on the same
manager.
🧹 Nitpick comments (1)
src/review-checkpoints.test.ts (1)

116-138: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a regression for eligibility retry after a diagnostic.

The added cases cover partial ref loss well. They do not cover a cached diagnostic. Add a case that initializes a repository without HEAD, creates the first commit, and then calls reviewChanges with the same manager. That case currently fails because the manager treats a defined diagnostic as initialized. See the related comment on src/review-checkpoints.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/review-checkpoints.test.ts` around lines 116 - 138, Extend the review
checkpoint tests with a cached-diagnostic regression: initialize a repository
without HEAD, create its first commit, then call reviewChanges again using the
same manager and verify eligibility succeeds. Anchor the case around
createReviewCheckpointManager, initializeWorkspace, and reviewChanges, ensuring
a defined diagnostic does not prevent retrying initialization after the
repository becomes eligible.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/review-checkpoints.ts`:
- Around line 58-63: Update src/review-checkpoints.ts: in the existing-state
early return around initializeWorkspace, stop treating existingState.diagnostic
as initialized, and change isInitializedState to check only gitRoot so failed
eligibility can be retried. Add a test in src/review-checkpoints.test.ts
covering initialization without HEAD, creating the first commit, then calling
reviewChanges on the same manager.

---

Nitpick comments:
In `@src/review-checkpoints.test.ts`:
- Around line 116-138: Extend the review checkpoint tests with a
cached-diagnostic regression: initialize a repository without HEAD, create its
first commit, then call reviewChanges again using the same manager and verify
eligibility succeeds. Anchor the case around createReviewCheckpointManager,
initializeWorkspace, and reviewChanges, ensuring a defined diagnostic does not
prevent retrying initialization after the repository becomes eligible.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fb610c40-bfbc-4c06-b9aa-7760d3140158

📥 Commits

Reviewing files that changed from the base of the PR and between 5f677b8 and ffb83de.

📒 Files selected for processing (16)
  • docs/chatgpt-coding-workflow.md
  • package.json
  • src/db/migrations.ts
  • src/db/schema.ts
  • src/oauth-store.test.ts
  • src/request-meta.test.ts
  • src/request-meta.ts
  • src/review-checkpoints.test.ts
  • src/review-checkpoints.ts
  • src/server.ts
  • src/ui/card-types.test.ts
  • src/ui/card-types.ts
  • src/ui/workspace-app.tsx
  • src/workspace-store.ts
  • src/workspaces.test.ts
  • src/workspaces.ts

Comment thread src/review-checkpoints.ts Outdated

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (2)
src/review-checkpoints.test.ts (1)

147-150: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tighten the rejection matcher for the unborn repository case.

The regex /commit|HEAD|Git/i matches every diagnostic this path can produce, including "workspace is not inside a git repository" and the generic fallback "show_changes requires a Git workspace in this version.". The test can then pass when eligibility fails for the wrong reason. getGitEligibility returns "repository has no HEAD commit" for an unborn repository, so assert that message.

♻️ Proposed change
   await assert.rejects(
     () => unbornManager.reviewChanges({ workspaceId: "ws_unborn", root: unbornRoot }),
-    /commit|HEAD|Git/i,
+    /no HEAD commit/,
   );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/review-checkpoints.test.ts` around lines 147 - 150, In the unborn
repository rejection test for unbornManager.reviewChanges, replace the broad
/commit|HEAD|Git/i matcher with an assertion targeting the exact
getGitEligibility message “repository has no HEAD commit,” so failures for
unrelated workspace or fallback diagnostics do not satisfy the test.
src/workspaces.ts (1)

439-453: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Avoid the in-place reverse() inside the loop.

missingSegments.reverse() mutates the array. Today it runs only on the successful iteration, because await realpath(candidate) is evaluated before the spread argument. That correctness depends on argument evaluation order relative to the await. A later reorder, for example hoisting the realpath result into a variable placed after the spread, would reverse the array on each retry and corrupt the key.

Use a non-mutating copy.

♻️ Proposed non-mutating variant
   while (true) {
     try {
-      return resolve(await realpath(candidate), ...missingSegments.reverse());
+      const resolvedPrefix = await realpath(candidate);
+      return resolve(resolvedPrefix, ...missingSegments.slice().reverse());
     } catch {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/workspaces.ts` around lines 439 - 453, Update canonicalPath to avoid
mutating missingSegments when restoring the collected path components: replace
the in-place reverse operation with a non-mutating reversed copy while
preserving the existing realpath retry and resolution behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/review-checkpoints.test.ts`:
- Around line 147-150: In the unborn repository rejection test for
unbornManager.reviewChanges, replace the broad /commit|HEAD|Git/i matcher with
an assertion targeting the exact getGitEligibility message “repository has no
HEAD commit,” so failures for unrelated workspace or fallback diagnostics do not
satisfy the test.

In `@src/workspaces.ts`:
- Around line 439-453: Update canonicalPath to avoid mutating missingSegments
when restoring the collected path components: replace the in-place reverse
operation with a non-mutating reversed copy while preserving the existing
realpath retry and resolution behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2a42c706-77cc-465a-af13-552506e7d878

📥 Commits

Reviewing files that changed from the base of the PR and between 5f677b8 and 598c737.

📒 Files selected for processing (16)
  • docs/chatgpt-coding-workflow.md
  • package.json
  • src/db/migrations.ts
  • src/db/schema.ts
  • src/oauth-store.test.ts
  • src/request-meta.test.ts
  • src/request-meta.ts
  • src/review-checkpoints.test.ts
  • src/review-checkpoints.ts
  • src/server.ts
  • src/ui/card-types.test.ts
  • src/ui/card-types.ts
  • src/ui/workspace-app.tsx
  • src/workspace-store.ts
  • src/workspaces.test.ts
  • src/workspaces.ts

@Waishnav

Waishnav commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

[GPT-5.6 Thinking] RESPONDING ON BEHALF OF WAISHNAV

Pushed 13f1464 on the final head. The public show_changes path now handles a missing last-shown checkpoint without exposing a new tool parameter: it safely falls back to the surviving workspace-open checkpoint, reports that fallback, and re-establishes the baseline when changes are marked reviewed.

Full tests, typecheck, production build, and diff checks pass locally.

@coderabbitai full review
@greptileai

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/workspaces.test.ts (1)

365-369: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Assert workspaceReused for the aliased open.

The aliased open is the case that must prove alias-to-canonical reuse. The test checks includeBootstrapContext and the workspace id, but not workspaceReused. The parallel stale-alias case at Line 381 does check it. Add the flag assertion so a regression that returns a new workspace with a copied id cannot pass.

♻️ Proposed additional assertion
   assert.equal(aliasedConversationWorkspace.includeBootstrapContext, false);
+  assert.equal(aliasedConversationWorkspace.workspaceReused, true);
   assert.equal(
     aliasedConversationWorkspace.workspace.id,
     directConversationWorkspace.workspace.id,
   );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/workspaces.test.ts` around lines 365 - 369, Add an assertion for the
workspaceReused flag in the aliased open test case at lines 365-369. Reference
the stale-alias case at line 381 which already includes this assertion and apply
the same pattern. This assertion is necessary to ensure the test catches
regressions where a new workspace is returned with a copied id instead of truly
reusing the canonical workspace through the alias.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/ui/workspace-app.tsx`:
- Around line 504-514: Update the unavailable-reason handling near agent
rendering in workspace-app.tsx to use a truthy fallback when
agent.providerUnavailableReason is empty, ensuring the unavailable marker
remains present instead of being removed by filter(Boolean).

---

Nitpick comments:
In `@src/workspaces.test.ts`:
- Around line 365-369: Add an assertion for the workspaceReused flag in the
aliased open test case at lines 365-369. Reference the stale-alias case at line
381 which already includes this assertion and apply the same pattern. This
assertion is necessary to ensure the test catches regressions where a new
workspace is returned with a copied id instead of truly reusing the canonical
workspace through the alias.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 56277d7e-f79f-4e3b-ab16-6ae88dbaf930

📥 Commits

Reviewing files that changed from the base of the PR and between 5f677b8 and 13f1464.

📒 Files selected for processing (16)
  • docs/chatgpt-coding-workflow.md
  • package.json
  • src/db/migrations.ts
  • src/db/schema.ts
  • src/oauth-store.test.ts
  • src/request-meta.test.ts
  • src/request-meta.ts
  • src/review-checkpoints.test.ts
  • src/review-checkpoints.ts
  • src/server.ts
  • src/ui/card-types.test.ts
  • src/ui/card-types.ts
  • src/ui/workspace-app.tsx
  • src/workspace-store.ts
  • src/workspaces.test.ts
  • src/workspaces.ts

Comment thread src/ui/workspace-app.tsx
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