Skip to content

perf(tui): replace reconcile() with path-syntax setStore for streaming updates#2

Closed
coleleavitt wants to merge 104 commits intodevfrom
refactor/sync-store-path-syntax
Closed

perf(tui): replace reconcile() with path-syntax setStore for streaming updates#2
coleleavitt wants to merge 104 commits intodevfrom
refactor/sync-store-path-syntax

Conversation

@coleleavitt
Copy link
Owner

@coleleavitt coleleavitt commented Feb 27, 2026

Summary

  • Replace all reconcile() calls in sync.tsx with direct assignment and path-syntax setStore() to eliminate O(n) deep-diffing on every streaming token event
  • Replace produce() delta flush on hot path with surgical setStore("part", msgID, idx, field, fn) — O(depth) ≈ O(1) instead of O(n)
  • Remove reconcile import entirely — no remaining uses

Problem

During LLM streaming (~100 message.part.delta events/sec), each token triggers a Solid.js store update via reconcile(). This function (proven from Solid.js source modifiers.ts:129-140) calls applyState() which recursively traverses the entire object tree — O(n) per update.

Combined with Bun's bmalloc SYSCALL spin loop on madvise(MADV_DONTNEED) EAGAIN, the excessive object allocations from reconcile trigger GC which triggers bmalloc's zero-delay retry loop, causing 100% CPU and TUI freeze.

Root Cause Analysis

Solid.js Store API Complexity (from source analysis)

API Complexity Source Location Use For
reconcile() O(n) tree walk modifiers.ts:129-140 API response replacement ONLY
setStore() path-syntax O(depth) ≈ O(1) store.ts:269-313 Surgical streaming updates
produce() O(k) mutations modifiers.ts:166-180 Complex multi-field updates

Mutation Audit (46 total in sync.tsx)

  • 5 reconcile() on HOT PATH → replaced with direct assignment
  • 1 produce() delta on HOT PATH → replaced with setStore path-syntax
  • 14 reconcile() in BOOTSTRAP → replaced with direct assignment
  • 10 produce() for sorted splices → kept (correct usage)
  • 16 already correct (path-syntax)

Part of a 3-layer fix

  1. Bun/WebKit (bmalloc SYSCALL EAGAIN spin loop): oven-sh/WebKit#169
  2. opencode (this PR): reconcile→path-syntax to reduce GC pressure
  3. oh-my-opencode: subscription-based hook dispatcher to reduce async dispatch from ~2100/sec to ~200/sec

Verification

  • TypeScript typecheck: ✅ (bun turbo typecheck — all 3 packages pass)
  • Build: ✅ (bun turbo build — all packages pass)
  • LSP diagnostics: ✅ Clean

Summary by cubic

Replaced Solid store reconcile() with path-syntax setStore and direct assignment on streaming paths to cut per-token work and prevent CPU spikes/freezes during LLM streaming. Also simplified review UI and improved comment/session handling.

  • Refactors

    • Switched reconcile()/produce() on hot paths to setStore path-syntax; removed reconcile import.
    • Added request-tree helpers to surface permission/question blocks from child sessions.
    • Migrated Code → File viewer, removed file tree tooltips, simplified side panel/review layout, and updated tab styling.
    • Added comment metadata to prompt parts plus comment update/remove APIs; improved selection cloning and history.
  • Bug Fixes

    • Ignored stale part deltas and coalesced streaming events for smoother updates.
    • Normalized Windows paths, switched tests to 127.0.0.1, and added Windows to unit test matrix.
    • Improved error toasts with server error formatter (handles ConfigInvalidError).
    • Fixed keyboard navigation between messages, hash-scroll offset with sticky headers, and open-review timing.
    • CI: use Bun baseline download and bump to 1.3.10.

Written for commit 56e7057. Summary will update on new commits.

R44VC0RP and others added 30 commits February 23, 2026 16:51
…o#14079)

Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
nexxeln and others added 15 commits February 26, 2026 15:42
Co-authored-by: David Hill <iamdavidhill@gmail.com>
Co-authored-by: adamelmore <2363879+adamdottv@users.noreply.github.com>
Co-authored-by: David Hill <iamdavidhill@gmail.com>
…g updates

Replace all reconcile() calls with direct assignment and path-syntax
setStore() to eliminate O(n) deep-diff overhead during streaming.

- Hot path: reconcile(info) → direct assignment for message/part/session updates
- Hot path: produce() delta flush → setStore path-syntax (O(depth) vs O(n))
- Bootstrap: remove reconcile() from all one-shot API response stores
- Remove unused reconcile import from solid-js/store

Proven from Solid.js source: reconcile() recursively traverses the
entire object tree (applyState in modifiers.ts), while path-syntax
navigates directly to the leaf node via updatePath (store.ts).
@kusari-inspector
Copy link

⚠️ Workspace Mapping Required

Hello! We noticed that your GitHub organization is not yet mapped to a Kusari workspace. Kusari Inspector now requires installations to be associated with a Kusari workspace.

⚠️ NOTE: Only the admin who installed the Kusari GitHub App can complete these steps. If the admin is unable to complete these steps, please contact support@kusari.dev

To complete the setup:

  1. Visit https://console.us.kusari.cloud/auth/github and log in via github
  2. If you have only one workspace, it will be automatically selected for you
  3. Once the mapping is complete, return here and create a new comment with: @kusari-inspector re-run

This will trigger the analysis to run again.

For more information, or if you need help, visit https://github.com/kusaridev/community/discussions

@devloai
Copy link

devloai bot commented Feb 27, 2026

Unable to trigger custom agent "Code Reviewer". You have run out of credits 😔
Please upgrade your plan or buy additional credits from the subscription page.

@github-actions
Copy link

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions
Copy link

Hey! Your PR title perf(tui): replace reconcile() with path-syntax setStore for streaming updates doesn't follow conventional commit format.

Please update it to start with one of:

  • feat: or feat(scope): new feature
  • fix: or fix(scope): bug fix
  • docs: or docs(scope): documentation changes
  • chore: or chore(scope): maintenance tasks
  • refactor: or refactor(scope): code refactoring
  • test: or test(scope): adding or updating tests

Where scope is the package name (e.g., app, desktop, opencode).

See CONTRIBUTING.md for details.

@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

Important

Review skipped

Too many files!

This PR contains 275 files, which is 125 over the limit of 150.

📥 Commits

Reviewing files that changed from the base of the PR and between 8f2d8dd and 56e7057.

⛔ Files ignored due to path filters (25)
  • bun.lock is excluded by !**/*.lock
  • packages/desktop/src-tauri/Cargo.lock is excluded by !**/*.lock
  • packages/sdk/js/src/v2/gen/sdk.gen.ts is excluded by !**/gen/**
  • packages/sdk/js/src/v2/gen/types.gen.ts is excluded by !**/gen/**
  • packages/storybook/debug-storybook.log is excluded by !**/*.log
  • packages/ui/src/assets/icons/provider/302ai.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/berget.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/cloudferro-sherlock.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/firmware.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/gitlab.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/jiekou.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/kilo.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/kuae-cloud-coding-plan.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/meganova.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/minimax-cn-coding-plan.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/minimax-coding-plan.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/moark.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/nova.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/novita-ai.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/privatemode-ai.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/qihang-ai.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/qiniu-ai.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/stackit.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/stepfun.svg is excluded by !**/*.svg
  • packages/ui/src/assets/icons/provider/vivgrid.svg is excluded by !**/*.svg
📒 Files selected for processing (275)
  • .github/actions/setup-bun/action.yml
  • .github/workflows/beta.yml
  • .github/workflows/compliance-close.yml
  • .github/workflows/docs-locale-sync.yml
  • .github/workflows/pr-standards.yml
  • .github/workflows/test.yml
  • .github/workflows/vouch-check-issue.yml
  • .github/workflows/vouch-check-pr.yml
  • .github/workflows/vouch-manage-by-issue.yml
  • .opencode/agent/translator.md
  • .opencode/glossary/README.md
  • .opencode/glossary/ar.md
  • .opencode/glossary/br.md
  • .opencode/glossary/bs.md
  • .opencode/glossary/da.md
  • .opencode/glossary/de.md
  • .opencode/glossary/es.md
  • .opencode/glossary/fr.md
  • .opencode/glossary/ja.md
  • .opencode/glossary/ko.md
  • .opencode/glossary/no.md
  • .opencode/glossary/pl.md
  • .opencode/glossary/ru.md
  • .opencode/glossary/th.md
  • .opencode/glossary/zh-cn.md
  • .opencode/glossary/zh-tw.md
  • infra/console.ts
  • nix/hashes.json
  • package.json
  • packages/app/e2e/files/file-tree.spec.ts
  • packages/app/e2e/files/file-viewer.spec.ts
  • packages/app/e2e/projects/projects-switch.spec.ts
  • packages/app/e2e/session/session-composer-dock.spec.ts
  • packages/app/e2e/utils.ts
  • packages/app/package.json
  • packages/app/playwright.config.ts
  • packages/app/src/app.tsx
  • packages/app/src/components/dialog-select-model-unpaid.tsx
  • packages/app/src/components/dialog-select-provider.tsx
  • packages/app/src/components/file-tree.tsx
  • packages/app/src/components/prompt-input.tsx
  • packages/app/src/components/prompt-input/build-request-parts.test.ts
  • packages/app/src/components/prompt-input/build-request-parts.ts
  • packages/app/src/components/prompt-input/history.test.ts
  • packages/app/src/components/prompt-input/history.ts
  • packages/app/src/components/session/session-context-tab.tsx
  • packages/app/src/components/session/session-header.tsx
  • packages/app/src/components/session/session-sortable-tab.tsx
  • packages/app/src/components/settings-providers.tsx
  • packages/app/src/context/comments.test.ts
  • packages/app/src/context/comments.tsx
  • packages/app/src/context/file/path.test.ts
  • packages/app/src/context/file/path.ts
  • packages/app/src/context/file/view-cache.ts
  • packages/app/src/context/global-sdk.tsx
  • packages/app/src/context/global-sync.tsx
  • packages/app/src/context/global-sync/bootstrap.ts
  • packages/app/src/context/layout-scroll.test.ts
  • packages/app/src/context/layout-scroll.ts
  • packages/app/src/context/permission.tsx
  • packages/app/src/context/prompt.tsx
  • packages/app/src/hooks/use-providers.ts
  • packages/app/src/i18n/ar.ts
  • packages/app/src/i18n/br.ts
  • packages/app/src/i18n/bs.ts
  • packages/app/src/i18n/da.ts
  • packages/app/src/i18n/de.ts
  • packages/app/src/i18n/en.ts
  • packages/app/src/i18n/es.ts
  • packages/app/src/i18n/fr.ts
  • packages/app/src/i18n/ja.ts
  • packages/app/src/i18n/ko.ts
  • packages/app/src/i18n/no.ts
  • packages/app/src/i18n/pl.ts
  • packages/app/src/i18n/ru.ts
  • packages/app/src/i18n/th.ts
  • packages/app/src/i18n/zh.ts
  • packages/app/src/i18n/zht.ts
  • packages/app/src/pages/directory-layout.tsx
  • packages/app/src/pages/layout.tsx
  • packages/app/src/pages/layout/helpers.test.ts
  • packages/app/src/pages/layout/helpers.ts
  • packages/app/src/pages/layout/sidebar-project.tsx
  • packages/app/src/pages/session.tsx
  • packages/app/src/pages/session/composer/session-composer-state.test.ts
  • packages/app/src/pages/session/composer/session-composer-state.ts
  • packages/app/src/pages/session/composer/session-request-tree.ts
  • packages/app/src/pages/session/file-tabs.tsx
  • packages/app/src/pages/session/helpers.test.ts
  • packages/app/src/pages/session/helpers.ts
  • packages/app/src/pages/session/message-timeline.tsx
  • packages/app/src/pages/session/review-tab.tsx
  • packages/app/src/pages/session/session-side-panel.tsx
  • packages/app/src/pages/session/use-session-hash-scroll.ts
  • packages/app/src/utils/comment-note.ts
  • packages/app/src/utils/server-errors.test.ts
  • packages/app/src/utils/server-errors.ts
  • packages/console/app/package.json
  • packages/console/app/src/i18n/ar.ts
  • packages/console/app/src/i18n/br.ts
  • packages/console/app/src/i18n/da.ts
  • packages/console/app/src/i18n/de.ts
  • packages/console/app/src/i18n/en.ts
  • packages/console/app/src/i18n/es.ts
  • packages/console/app/src/i18n/fr.ts
  • packages/console/app/src/i18n/it.ts
  • packages/console/app/src/i18n/ja.ts
  • packages/console/app/src/i18n/ko.ts
  • packages/console/app/src/i18n/no.ts
  • packages/console/app/src/i18n/pl.ts
  • packages/console/app/src/i18n/ru.ts
  • packages/console/app/src/i18n/th.ts
  • packages/console/app/src/i18n/tr.ts
  • packages/console/app/src/i18n/zh.ts
  • packages/console/app/src/i18n/zht.ts
  • packages/console/app/src/routes/black.css
  • packages/console/app/src/routes/black/index.tsx
  • packages/console/app/src/routes/black/subscribe/[plan].tsx
  • packages/console/app/src/routes/stripe/webhook.ts
  • packages/console/app/src/routes/workspace/[id]/billing/black-section.tsx
  • packages/console/app/src/routes/workspace/[id]/billing/index.tsx
  • packages/console/app/src/routes/workspace/[id]/billing/lite-section.module.css
  • packages/console/app/src/routes/workspace/[id]/billing/lite-section.tsx
  • packages/console/app/src/routes/workspace/[id]/graph-section.tsx
  • packages/console/app/src/routes/workspace/[id]/usage-section.tsx
  • packages/console/app/src/routes/workspace/common.tsx
  • packages/console/app/src/routes/zen/go/v1/chat/completions.ts
  • packages/console/app/src/routes/zen/go/v1/messages.ts
  • packages/console/app/src/routes/zen/util/handler.ts
  • packages/console/app/src/routes/zen/v1/models.ts
  • packages/console/core/migrations/20260224043338_nifty_starjammers/migration.sql
  • packages/console/core/migrations/20260224043338_nifty_starjammers/snapshot.json
  • packages/console/core/package.json
  • packages/console/core/script/black-select-workspaces.ts
  • packages/console/core/script/lookup-user.ts
  • packages/console/core/src/billing.ts
  • packages/console/core/src/black.ts
  • packages/console/core/src/identifier.ts
  • packages/console/core/src/lite.ts
  • packages/console/core/src/schema/billing.sql.ts
  • packages/console/core/src/subscription.ts
  • packages/console/core/src/util/date.test.ts
  • packages/console/core/src/util/date.ts
  • packages/console/core/test/date.test.ts
  • packages/console/core/test/subscription.test.ts
  • packages/console/function/package.json
  • packages/console/function/src/log-processor.ts
  • packages/console/mail/package.json
  • packages/desktop/README.md
  • packages/desktop/package.json
  • packages/desktop/src-tauri/Cargo.toml
  • packages/desktop/src-tauri/src/cli.rs
  • packages/desktop/src-tauri/src/lib.rs
  • packages/desktop/src-tauri/src/os/mod.rs
  • packages/desktop/src-tauri/src/os/windows.rs
  • packages/desktop/src/bindings.ts
  • packages/desktop/src/index.tsx
  • packages/enterprise/package.json
  • packages/enterprise/src/routes/share/[shareID].tsx
  • packages/extensions/zed/extension.toml
  • packages/function/package.json
  • packages/opencode/BUN_SHELL_MIGRATION_PLAN.md
  • packages/opencode/package.json
  • packages/opencode/script/schema.ts
  • packages/opencode/src/acp/agent.ts
  • packages/opencode/src/bun/index.ts
  • packages/opencode/src/bun/registry.ts
  • packages/opencode/src/cli/cmd/auth.ts
  • packages/opencode/src/cli/cmd/session.ts
  • packages/opencode/src/cli/cmd/tui/app.tsx
  • packages/opencode/src/cli/cmd/tui/attach.ts
  • packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx
  • packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx
  • packages/opencode/src/cli/cmd/tui/component/tips.tsx
  • packages/opencode/src/cli/cmd/tui/context/keybind.tsx
  • packages/opencode/src/cli/cmd/tui/context/sync.tsx
  • packages/opencode/src/cli/cmd/tui/context/theme.tsx
  • packages/opencode/src/cli/cmd/tui/context/tui-config.tsx
  • packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
  • packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx
  • packages/opencode/src/cli/cmd/tui/thread.ts
  • packages/opencode/src/cli/cmd/tui/util/clipboard.ts
  • packages/opencode/src/cli/cmd/tui/util/editor.ts
  • packages/opencode/src/cli/cmd/workspace-serve.ts
  • packages/opencode/src/config/config.ts
  • packages/opencode/src/config/markdown.ts
  • packages/opencode/src/config/migrate-tui-config.ts
  • packages/opencode/src/config/paths.ts
  • packages/opencode/src/config/tui-schema.ts
  • packages/opencode/src/config/tui.ts
  • packages/opencode/src/file/ignore.ts
  • packages/opencode/src/file/ripgrep.ts
  • packages/opencode/src/file/time.ts
  • packages/opencode/src/flag/flag.ts
  • packages/opencode/src/format/formatter.ts
  • packages/opencode/src/format/index.ts
  • packages/opencode/src/index.ts
  • packages/opencode/src/lsp/server.ts
  • packages/opencode/src/project/project.ts
  • packages/opencode/src/pty/index.ts
  • packages/opencode/src/server/routes/session.ts
  • packages/opencode/src/session/index.ts
  • packages/opencode/src/snapshot/index.ts
  • packages/opencode/src/storage/db.ts
  • packages/opencode/src/tool/grep.ts
  • packages/opencode/src/tool/plan.ts
  • packages/opencode/src/tool/registry.ts
  • packages/opencode/src/util/git.ts
  • packages/opencode/src/util/process.ts
  • packages/opencode/test/acp/event-subscription.test.ts
  • packages/opencode/test/config/config.test.ts
  • packages/opencode/test/config/markdown.test.ts
  • packages/opencode/test/config/tui.test.ts
  • packages/opencode/test/ide/ide.test.ts
  • packages/opencode/test/preload.ts
  • packages/opencode/test/pty/pty-output-isolation.test.ts
  • packages/opencode/test/skill/discovery.test.ts
  • packages/opencode/test/snapshot/snapshot.test.ts
  • packages/opencode/test/tool/bash.test.ts
  • packages/opencode/test/tool/external-directory.test.ts
  • packages/opencode/test/tool/write.test.ts
  • packages/opencode/test/util/glob.test.ts
  • packages/opencode/test/util/process.test.ts
  • packages/plugin/package.json
  • packages/plugin/script/publish.ts
  • packages/sdk/js/package.json
  • packages/sdk/js/script/build.ts
  • packages/sdk/js/script/publish.ts
  • packages/sdk/openapi.json
  • packages/slack/package.json
  • packages/storybook/.gitignore
  • packages/storybook/.storybook/main.ts
  • packages/storybook/.storybook/manager.ts
  • packages/storybook/.storybook/preview.tsx
  • packages/storybook/.storybook/theme-tool.ts
  • packages/storybook/package.json
  • packages/storybook/tsconfig.json
  • packages/ui/package.json
  • packages/ui/src/components/accordion.stories.tsx
  • packages/ui/src/components/app-icon.stories.tsx
  • packages/ui/src/components/avatar.stories.tsx
  • packages/ui/src/components/basic-tool.stories.tsx
  • packages/ui/src/components/button.stories.tsx
  • packages/ui/src/components/card.stories.tsx
  • packages/ui/src/components/checkbox.stories.tsx
  • packages/ui/src/components/code.css
  • packages/ui/src/components/code.stories.tsx
  • packages/ui/src/components/code.tsx
  • packages/ui/src/components/collapsible.stories.tsx
  • packages/ui/src/components/context-menu.stories.tsx
  • packages/ui/src/components/dialog.stories.tsx
  • packages/ui/src/components/diff-changes.stories.tsx
  • packages/ui/src/components/diff-ssr.stories.tsx
  • packages/ui/src/components/diff-ssr.tsx
  • packages/ui/src/components/diff.stories.tsx
  • packages/ui/src/components/diff.tsx
  • packages/ui/src/components/dock-prompt.stories.tsx
  • packages/ui/src/components/dropdown-menu.stories.tsx
  • packages/ui/src/components/favicon.stories.tsx
  • packages/ui/src/components/file-icon.stories.tsx
  • packages/ui/src/components/file-media.tsx
  • packages/ui/src/components/file-search.tsx
  • packages/ui/src/components/file-ssr.tsx
  • packages/ui/src/components/file.css
  • packages/ui/src/components/file.tsx
  • packages/ui/src/components/font.stories.tsx
  • packages/ui/src/components/hover-card.stories.tsx
  • packages/ui/src/components/icon-button.stories.tsx
  • packages/ui/src/components/icon.stories.tsx
  • packages/ui/src/components/image-preview.stories.tsx
  • packages/ui/src/components/inline-input.stories.tsx
  • packages/ui/src/components/keybind.stories.tsx
  • packages/ui/src/components/line-comment-annotations.tsx
  • packages/ui/src/components/line-comment-styles.ts
  • packages/ui/src/components/line-comment.stories.tsx

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/sync-store-path-syntax

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

@qodo-free-for-open-source-projects

Review Summary by Qodo

Multi-feature release: Go subscription tier, TUI config separation, file search, comment system, and cross-platform improvements

✨ Enhancement 🧪 Tests 📝 Documentation

Grey Divider

Walkthroughs

Description
  **Major Features:**
• Introduced "Go" subscription tier (formerly "Lite") with comprehensive billing, validation, and UI
  support across all languages
• Added file find/search functionality with keyboard shortcuts and dual rendering modes (CSS
  Highlights API + DOM overlay fallback)
• Implemented prompt history with inline comment tracking and metadata persistence
• Added code comment utilities with metadata serialization and file selection support
• Introduced line selection and diff selection utilities for shadow DOM contexts
  **Configuration & Infrastructure:**
• Separated TUI configuration from main config schema into dedicated tui.json files with migration
  support for legacy keys
• Added ConfigPaths module for file discovery and JSONC parsing with environment variable and file
  substitution
• Refactored schema generation to support multiple config types (Config.Info and TuiConfig.Info)
• Enhanced git configuration with support for long paths and symlinks in snapshot management
• Added Process utility module for cross-platform command execution with improved error handling
  **Tool & Agent Improvements:**
• Implemented tool execution state tracking and bash output deduplication to prevent duplicate
  snapshots
• Added synthetic pending state emission before first running tool update
• Enhanced tool event handling with metadata output extraction
  **Testing & Quality:**
• Added comprehensive TUI configuration test suite covering precedence and migration
• Improved snapshot tests cross-platform compatibility with forward slash path normalization
• Added subscription monthly usage analysis and date utility function tests
• Refactored E2E permission testing with mock-based approach
• Extended tool event streaming and deduplication test coverage
• Added session request tree and latest root session tests
  **UI & Styling:**
• Implemented line comment component styling with comprehensive CSS and installation utilities
• Added file viewer runtime utilities for shadow DOM readiness and color scheme synchronization
• Created commented lines marking utilities for diff and file views
• Added media type detection and data URL generation utilities
• Integrated line comment styles into Pierre diff component
  **Internationalization:**
• Added Go subscription translations across 15+ languages (English, Spanish, French, German,
  Italian, Portuguese, Russian, Chinese, Japanese, Korean, Thai, Turkish, Polish, Danish, Norwegian,
  Arabic, Bosnian)
• Updated permission-related translations to use "permissions" terminology
• Added provider tagline translations for OpenCode and OpenCode Go
  **Process & Tooling:**
• Replaced Bun.spawn() calls with new Process utility for consistent process handling across LSP
  server, ripgrep, and git operations
• Simplified PTY subscriber tracking by removing complex socket tagging logic
• Added automatic merge conflict resolution in beta script using OpenCode AI
• Updated Stripe product naming from "OpenCode Lite" to "OpenCode Go"
Diagram
flowchart LR
  A["Legacy Config<br/>opencode.json"] -->|"migrate"| B["TUI Config<br/>tui.json"]
  C["ConfigPaths<br/>Discovery"] -->|"load"| D["TUI Config<br/>Loader"]
  E["File Find<br/>Module"] -->|"render"| F["CSS Highlights<br/>+ DOM Overlay"]
  G["Comment<br/>Metadata"] -->|"attach"| H["Prompt History<br/>+ Request Parts"]
  I["Tool Events"] -->|"deduplicate"| J["Bash Output<br/>Snapshots"]
  K["Process<br/>Utility"] -->|"replace"| L["Bun.spawn<br/>Calls"]
  M["Go Subscription<br/>Tier"] -->|"support"| N["Billing + UI<br/>+ i18n"]
Loading

Grey Divider

File Changes

1. packages/opencode/test/config/tui.test.ts 🧪 Tests +510/-0

Add comprehensive TUI configuration test suite

• New comprehensive test suite for TUI configuration loading with 510 lines of test cases
• Tests cover configuration precedence (global, project, local, managed, environment variables)
• Tests validate migration of legacy TUI keys from opencode.json to tui.json
• Tests verify file and environment variable substitutions, keybind merging, and graceful fallbacks

packages/opencode/test/config/tui.test.ts


2. packages/ui/src/pierre/file-find.ts ✨ Enhancement +576/-0

Implement file find/search functionality module

• New 576-line module implementing file search/find functionality with Solid.js
• Provides createFileFind() function for text search in code diffs with overlay and highlight
 modes
• Supports keyboard shortcuts (Ctrl+F/Cmd+F for open, Ctrl+G/Cmd+G for next), navigation, and scroll
 tracking
• Implements both CSS Highlights API (modern) and DOM overlay (fallback) rendering modes

packages/ui/src/pierre/file-find.ts


3. packages/console/app/src/routes/stripe/webhook.ts ✨ Enhancement +73/-289

Add lite subscription tier support to Stripe webhook

• Refactored webhook handler to support "lite" subscription tier alongside existing "black"
 subscriptions
• Replaced generic SubscriptionTable references with tier-specific LiteTable and
 SubscriptionTable (renamed to black)
• Updated customer.subscription.created event to handle lite subscriptions with separate billing
 logic
• Modified customer.subscription.updated/deleted events to route to tier-specific unsubscribe
 handlers

packages/console/app/src/routes/stripe/webhook.ts


View more (162)
4. packages/opencode/src/config/config.ts Refactoring +56/-148

Separate TUI configuration from main config schema

• Extracted TUI configuration (theme, keybinds, tui fields) from main Config.Info schema to
 separate tui.json handling
• Refactored config loading to use new ConfigPaths module for file discovery and parsing
• Renamed getManagedConfigDir() to systemManagedConfigDir() and made managedConfigDir()
 exportable
• Added deprecation warning for TUI keys in opencode.json and improved plugin resolution with
 fallback to createRequire()

packages/opencode/src/config/config.ts


5. packages/console/app/src/routes/zen/util/handler.ts ✨ Enhancement +203/-59

Add lite subscription billing validation and tracking

• Added support for "lite" subscription billing source alongside existing "subscription" and "free"
 sources
• Implemented lite subscription validation with weekly, monthly, and rolling usage limits
• Renamed FREE_WORKSPACES to ADMIN_WORKSPACES and added alpha model access restrictions
• Updated usage tracking to handle both black and lite subscription tiers with tier-specific
 database updates

packages/console/app/src/routes/zen/util/handler.ts


6. packages/app/e2e/session/session-composer-dock.spec.ts 🧪 Tests +246/-52

Refactor permission testing with mock-based approach

• Refactored permission testing to use new withMockPermission() helper for mocking permission
 requests
• Added clearPermissionDock() helper to handle multiple permission dock interactions
• Replaced seedSessionPermission() calls with mock-based approach using page route interception
• Added new tests for child session question and permission blocking of parent dock

packages/app/e2e/session/session-composer-dock.spec.ts


7. packages/opencode/test/snapshot/snapshot.test.ts 🧪 Tests +47/-41

Improve snapshot tests cross-platform compatibility

• Added fwd() helper function to normalize file paths to forward slashes for cross-platform test
 assertions
• Replaced shell command symlink creation (ln -s) with fs.symlink() API for better
 cross-platform compatibility
• Updated all file path assertions to use fwd() helper for Windows path compatibility
• Fixed git config path handling to use forward slashes

packages/opencode/test/snapshot/snapshot.test.ts


8. packages/sdk/js/src/v2/gen/types.gen.ts Refactoring +40/-409

Remove TUI types and add message deletion endpoint types

• Removed KeybindsConfig type definition (388 lines) from generated SDK types
• Removed theme, keybinds, and tui fields from Config type definition
• Added new SessionDeleteMessageData, SessionDeleteMessageErrors, and
 SessionDeleteMessageResponse types for message deletion endpoint

packages/sdk/js/src/v2/gen/types.gen.ts


9. infra/console.ts ⚙️ Configuration changes +1/-1

Update Stripe product naming

• Changed Stripe product name from "OpenCode Lite" to "OpenCode Go"

infra/console.ts


10. packages/opencode/test/acp/event-subscription.test.ts 🧪 Tests +247/-2

Tool event streaming and deduplication test coverage

• Added helper functions inProgressText(), isToolCallUpdate(), and toolEvent() to construct
 and validate tool event payloads
• Extended createFakeAgent() to track sessionUpdates for assertion in tests
• Added four new test cases covering bash output snapshot deduplication, synthetic pending emission,
 duplicate prevention after replay, and snapshot clearing on pending state

packages/opencode/test/acp/event-subscription.test.ts


11. packages/app/src/components/prompt-input/history.ts ✨ Enhancement +106/-19

Prompt history with inline comment tracking

• Introduced PromptHistoryComment and PromptHistoryEntry types to associate comments with
 prompts in history
• Added PromptHistoryStoredEntry union type supporting both legacy Prompt arrays and new
 PromptHistoryEntry objects
• Implemented clonePromptHistoryComments(), normalizePromptHistoryEntry(), and
 isCommentEqual() helper functions
• Updated prependHistoryEntry() and navigatePromptHistory() to handle comments alongside prompts

packages/app/src/components/prompt-input/history.ts


12. packages/opencode/src/acp/agent.ts ✨ Enhancement +94/-51

Tool execution state tracking and bash output deduplication

• Added bashSnapshots and toolStarts maps to track tool execution state and deduplicate bash
 output
• Implemented bashOutput() helper to extract metadata output from tool parts
• Implemented toolStart() method to emit synthetic pending state before first running update
• Enhanced tool event handling to include bash output in tool_call_update and skip duplicate
 snapshots
• Added metadata to error state rawOutput

packages/opencode/src/acp/agent.ts


13. packages/opencode/src/lsp/server.ts Refactoring +24/-24

LSP server process spawning abstraction

• Replaced Bun.spawn() calls with Process.spawn() wrapper for consistent process spawning
• Changed readableStreamToText() import from bun to text() from node:stream/consumers
• Updated process invocation syntax from object form { cmd: [...] } to array form [...]
• Added null checks for proc.stdout before consuming stream

packages/opencode/src/lsp/server.ts


14. packages/opencode/src/snapshot/index.ts ⚙️ Configuration changes +20/-13

Git configuration for long paths and symlinks support

• Added git config options core.longpaths=true, core.symlinks=true, and core.fsmonitor=false
 to snapshot initialization
• Applied these config options consistently across all git operations (diff, restore, checkout,
 ls-tree, show, add)
• Enables support for long file paths and symlinks in snapshot management

packages/opencode/src/snapshot/index.ts


15. packages/opencode/test/config/config.test.ts 🧪 Tests +38/-18

Config tests for legacy TUI key migration

• Added test for ignoring legacy theme and tui keys in opencode config
• Updated environment variable substitution tests to use username instead of theme
• Updated file inclusion substitution tests to use username instead of theme
• Fixed test using Filesystem.write() instead of Bun.write() for consistency
• Removed theme assertions from managed settings override test

packages/opencode/test/config/config.test.ts


16. packages/opencode/src/config/paths.ts ✨ Enhancement +174/-0

Config file discovery and JSONC parsing utilities

• New module providing config file discovery and parsing utilities
• Implements projectFiles() to find config files up the directory tree
• Implements directories() to collect config directories from project, home, and flags
• Provides readFile(), parseText() with JSONC support and substitution of {env:VAR} and
 {file:path} tokens
• Defines JsonError and InvalidError custom error types

packages/opencode/src/config/paths.ts


17. packages/opencode/src/config/migrate-tui-config.ts ✨ Enhancement +155/-0

Legacy TUI configuration migration utility

• New module for migrating legacy theme, keybinds, and tui keys from opencode.json to
 dedicated tui.json files
• Implements migrateTuiConfig() to process config directories and extract TUI-specific settings
• Creates backup files before stripping legacy keys from source configs
• Normalizes legacy tui object structure using TuiLegacy schema

packages/opencode/src/config/migrate-tui-config.ts


18. packages/opencode/src/pty/index.ts Refactoring +19/-85

PTY subscriber tracking simplification

• Removed complex Subscriber type and socket tagging logic (tagSocket(), token() functions)
• Simplified subscriber tracking from Map<Socket, Subscriber> to Map<unknown, Socket>
• Changed connection key from socket ID/token to ws.data object reference
• Simplified cleanup logic to check ws.data === key instead of socket ID/token matching

packages/opencode/src/pty/index.ts


19. packages/ui/src/components/line-comment-styles.ts ✨ Enhancement +111/-3

Line comment component styling and installation

• Converted line comment styles from CSS string to exported constant with comprehensive styling
• Added styles for inline comments, add variant, popover positioning, and action buttons
• Implemented installLineCommentStyles() function to inject styles into document head
• Added support for comment head layout, tools section, and various button variants

packages/ui/src/components/line-comment-styles.ts


20. packages/console/core/test/subscription.test.ts 🧪 Tests +106/-0

Subscription monthly usage analysis tests

• New test suite for Subscription.analyzeMonthlyUsage() function
• Tests cover usage percentage calculation, rate-limiting at limit, monthly boundary resets, and
 edge cases
• Validates handling of subscription day 31 in short months
• Uses setSystemTime() for time-based test control

packages/console/core/test/subscription.test.ts


21. packages/ui/src/pierre/index.ts ✨ Enhancement +37/-5

Pierre diff component line comment integration

• Added import of lineCommentStyles from line-comment-styles module
• Added onLineNumberSelectionEnd callback prop to DiffProps type
• Extended CSS selectors to apply diff styles to both [data-diff] and [data-file] elements
• Integrated lineCommentStyles into the unsafeCSS template

packages/ui/src/pierre/index.ts


22. packages/ui/src/pierre/media.ts ✨ Enhancement +110/-0

Media type detection and data URL utilities

• New module for media type detection and data URL generation from file content
• Implements mediaKindFromPath() to detect image, audio, or SVG from file extension
• Implements dataUrlFromMediaValue() to convert file content to base64 data URLs
• Provides svgTextFromValue() and hasMediaValue() utilities for SVG and content validation
• Normalizes MIME types and handles base64 encoding/decoding

packages/ui/src/pierre/media.ts


23. packages/console/app/src/i18n/th.ts 📝 Documentation +35/-1

Thai i18n for Black pause and Go subscription

• Added black.paused translation for Black plan enrollment pause message
• Updated subscription labels from generic "สมัครสมาชิก" to plan-specific "Black", "Go", "BYOK"
• Added workspace.cost.liteShort translation for lite plan abbreviation
• Added comprehensive workspace.lite.* translations for Go subscription UI (20+ new keys)

packages/console/app/src/i18n/th.ts


24. packages/console/app/src/i18n/ja.ts 📝 Documentation +35/-1

Japanese i18n for Black pause and Go subscription

• Added black.paused translation for Black plan enrollment pause message
• Updated subscription labels from generic "サブスクリプション" to plan-specific "Black", "Go", "BYOK"
• Added workspace.cost.liteShort translation for lite plan abbreviation
• Added comprehensive workspace.lite.* translations for Go subscription UI (20+ new keys)

packages/console/app/src/i18n/ja.ts


25. packages/console/app/src/i18n/pl.ts 📝 Documentation +35/-1

Polish i18n for Black pause and Go subscription

• Added black.paused translation for Black plan enrollment pause message
• Updated subscription labels from generic "subskrypcja" to plan-specific "Black", "Go", "BYOK"
• Added workspace.cost.liteShort translation for lite plan abbreviation
• Added comprehensive workspace.lite.* translations for Go subscription UI (20+ new keys)

packages/console/app/src/i18n/pl.ts


26. packages/opencode/src/config/tui.ts ✨ Enhancement +118/-0

TUI configuration loading and merging

• New module for loading and merging TUI configuration from multiple sources
• Implements TuiConfig.get() to load config from global, project, custom, and managed directories
• Integrates migrateTuiConfig() to handle legacy opencode.json TUI keys
• Normalizes nested tui key structure for backward compatibility
• Validates config against TuiInfo schema

packages/opencode/src/config/tui.ts


27. packages/console/core/test/date.test.ts 🧪 Tests +76/-0

Date utility functions test coverage

• New test suite for getWeekBounds() and getMonthlyBounds() date utility functions
• Tests cover Monday-based week calculation, monthly boundary resets on subscription day, and short
 month handling
• Validates edge cases like subscription day 31 in February and exact reset boundary conditions

packages/console/core/test/date.test.ts


28. packages/ui/src/pierre/selection-bridge.ts ✨ Enhancement +129/-0

Line selection state management for diffs

• New module for managing line selection state in diff/file viewers
• Implements formatSelectedLineLabel() and previewSelectedLines() for selection display
• Implements createLineNumberSelectionBridge() to track pointer-based line selection with drag
 support
• Provides utilities for range cloning, line membership testing, and shadow DOM selection
 restoration

packages/ui/src/pierre/selection-bridge.ts


29. packages/console/app/src/i18n/tr.ts 📝 Documentation +35/-1

Turkish i18n for Black pause and Go subscription

• Added black.paused translation for Black plan enrollment pause message
• Updated subscription labels from generic "abonelik" to plan-specific "Black", "Go", "BYOK"
• Added workspace.cost.liteShort translation for lite plan abbreviation
• Added comprehensive workspace.lite.* translations for Go subscription UI (20+ new keys)

packages/console/app/src/i18n/tr.ts


30. packages/app/e2e/projects/projects-switch.spec.ts 🧪 Tests +11/-11

E2E test refactoring for session creation

• Refactored session creation to use SDK instead of UI interaction
• Added explicit error handling for workspace slug decoding
• Replaced prompt-based session creation with direct SDK call and navigation
• Updated URL assertions to use sessionIDFromUrl() polling for reliability

packages/app/e2e/projects/projects-switch.spec.ts


31. packages/app/src/components/prompt-input/build-request-parts.test.ts 🧪 Tests +9/-0

Request parts comment metadata assertion

• Added assertion to verify synthetic text parts include opencodeComment metadata with comment
 content
• Validates that comment text "check this" is properly attached to request parts

packages/app/src/components/prompt-input/build-request-parts.test.ts


32. packages/desktop/src-tauri/src/os/mod.rs ✨ Enhancement +2/-0

OS module structure for platform-specific code

• New module file for OS-specific functionality
• Conditionally includes Windows-specific module

packages/desktop/src-tauri/src/os/mod.rs


33. packages/console/app/src/i18n/zht.ts 📝 Documentation +34/-1

Add Traditional Chinese translations for Go subscription

• Added black.paused translation for Black plan enrollment pause message
• Updated subscription-related labels to distinguish between Black, Go, and BYOK plans
• Added comprehensive workspace.lite.* translation keys for the new Go subscription feature (30+
 new keys covering loading, time units, subscription management, and promotional content)

packages/console/app/src/i18n/zht.ts


34. packages/console/app/src/i18n/ar.ts 📝 Documentation +35/-1

Add Arabic translations for Go subscription feature

• Added black.paused Arabic translation for Black plan enrollment pause
• Updated subscription labels to differentiate Black, Go, and BYOK plans
• Added 31 new workspace.lite.* translation keys for Go subscription feature in Arabic

packages/console/app/src/i18n/ar.ts


35. packages/console/app/src/i18n/ru.ts 📝 Documentation +35/-1

Add Russian translations for Go subscription

• Added black.paused Russian translation for Black plan enrollment pause
• Updated subscription plan labels to distinguish Black, Go, and BYOK
• Added 31 new workspace.lite.* translation keys for Go subscription in Russian

packages/console/app/src/i18n/ru.ts


36. packages/app/src/components/prompt-input/history.test.ts 🧪 Tests +51/-1

Add comment support to prompt history tests

• Added normalizePromptHistoryEntry import and test for legacy prompt array support
• Added PromptHistoryComment type import and helper function to create comment objects
• Extended tests to verify comment handling in history entries, including deduplication and
 navigation with comments
• Added test for normalizePromptHistoryEntry to ensure backward compatibility with legacy prompt
 arrays

packages/app/src/components/prompt-input/history.test.ts


37. packages/console/app/src/i18n/it.ts 📝 Documentation +35/-1

Add Italian translations for Go subscription

• Added black.paused Italian translation for Black plan enrollment pause
• Updated subscription labels to distinguish Black, Go, and BYOK plans
• Added 31 new workspace.lite.* translation keys for Go subscription feature in Italian

packages/console/app/src/i18n/it.ts


38. packages/console/app/src/i18n/ko.ts 📝 Documentation +35/-1

Add Korean translations for Go subscription

• Added black.paused Korean translation for Black plan enrollment pause
• Updated subscription plan labels to differentiate Black, Go, and BYOK
• Added 31 new workspace.lite.* translation keys for Go subscription in Korean

packages/console/app/src/i18n/ko.ts


39. packages/console/app/src/i18n/da.ts 📝 Documentation +35/-1

Add Danish translations for Go subscription

• Added black.paused Danish translation for Black plan enrollment pause
• Updated subscription labels to distinguish Black, Go, and BYOK plans
• Added 31 new workspace.lite.* translation keys for Go subscription feature in Danish

packages/console/app/src/i18n/da.ts


40. packages/opencode/src/util/process.ts ✨ Enhancement +126/-0

Add Process utility module for command execution

• New utility module providing cross-platform process spawning and execution with Process.spawn()
 and Process.run() functions
• Supports abort signals, custom timeouts, signal handling, and environment variable merging
• Includes RunFailedError exception class with detailed error information (exit code, stdout,
 stderr)
• Handles both streaming (via exited promise) and buffered output modes

packages/opencode/src/util/process.ts


41. packages/console/app/src/i18n/no.ts 📝 Documentation +35/-1

Add Norwegian translations for Go subscription

• Added black.paused Norwegian translation for Black plan enrollment pause
• Updated subscription labels to distinguish Black, Go, and BYOK plans
• Added 31 new workspace.lite.* translation keys for Go subscription feature in Norwegian

packages/console/app/src/i18n/no.ts


42. packages/console/app/src/i18n/fr.ts 📝 Documentation +36/-1

Add French translations for Go subscription

• Added black.paused French translation for Black plan enrollment pause
• Updated subscription labels to differentiate Black, Go, and BYOK plans
• Added 32 new workspace.lite.* translation keys for Go subscription feature in French

packages/console/app/src/i18n/fr.ts


43. packages/console/app/src/i18n/de.ts 📝 Documentation +35/-1

Add German translations for Go subscription

• Added black.paused German translation for Black plan enrollment pause
• Updated subscription labels to distinguish Black, Go, and BYOK plans
• Added 31 new workspace.lite.* translation keys for Go subscription feature in German

packages/console/app/src/i18n/de.ts


44. packages/console/app/src/i18n/es.ts 📝 Documentation +35/-1

Add Spanish translations for Go subscription

• Added black.paused Spanish translation for Black plan enrollment pause
• Updated subscription labels to differentiate Black, Go, and BYOK plans
• Added 31 new workspace.lite.* translation keys for Go subscription feature in Spanish

packages/console/app/src/i18n/es.ts


45. packages/console/app/src/i18n/br.ts 📝 Documentation +35/-1

Add Brazilian Portuguese translations for Go subscription

• Added black.paused Brazilian Portuguese translation for Black plan enrollment pause
• Updated subscription labels to distinguish Black, Go, and BYOK plans
• Added 31 new workspace.lite.* translation keys for Go subscription feature in Brazilian
 Portuguese

packages/console/app/src/i18n/br.ts


46. packages/console/app/src/i18n/zh.ts 📝 Documentation +34/-1

Add Simplified Chinese translations for Go subscription

• Added black.paused Simplified Chinese translation for Black plan enrollment pause
• Updated subscription labels to differentiate Black, Go, and BYOK plans
• Added 30 new workspace.lite.* translation keys for Go subscription feature in Simplified Chinese

packages/console/app/src/i18n/zh.ts


47. packages/opencode/script/schema.ts ✨ Enhancement +53/-37

Refactor schema generation to support multiple configs

• Refactored schema generation into reusable generate() function to support multiple schema types
• Added support for generating TuiConfig.Info schema in addition to Config.Info
• Accepts two command-line arguments for config and TUI schema output files
• Preserves strictness with additionalProperties: false and adds example values from defaults

packages/opencode/script/schema.ts


48. packages/console/core/src/billing.ts ✨ Enhancement +79/-3

Add Go subscription billing functions

• Added LiteTable import and LiteData import for new Go subscription support
• Added generateLiteCheckoutUrl() function to create Stripe checkout sessions for Go subscriptions
• Renamed subscribe() to subscribeBlack() and unsubscribe() to unsubscribeBlack() for
 clarity
• Added unsubscribeLite() function to handle Go subscription cancellation and cleanup

packages/console/core/src/billing.ts


49. packages/console/app/src/i18n/en.ts 📝 Documentation +35/-1

Add English translations for Go subscription

• Added black.paused English translation for Black plan enrollment pause message
• Updated subscription labels to distinguish between Black, Go, and BYOK plans
• Added 31 new workspace.lite.* translation keys for Go subscription feature (loading, time units,
 subscription management, promotional content)

packages/console/app/src/i18n/en.ts


50. packages/opencode/src/file/ripgrep.ts Refactoring +27/-30

Replace Bun.spawn with Process utility

• Replaced Bun.spawn() calls with new Process.spawn() utility for consistent process handling
• Updated ripgrep extraction to use Process.spawn() with improved error handling
• Refactored ripgrep search to use Process.spawn() with async iteration over stdout instead of
 manual reader management
• Simplified stream consumption using text() helper from node:stream/consumers

packages/opencode/src/file/ripgrep.ts


51. packages/ui/src/pierre/commented-lines.ts ✨ Enhancement +91/-0

Add commented lines marking utilities

• New module providing functions to mark commented diff and file lines in shadow DOM
• Exports markCommentedDiffLines() for split/unified diff views and markCommentedFileLines() for
 plain file views
• Handles line annotation elements and supports both additions and deletions sides in diffs
• Uses data-comment-selected attribute to mark commented lines

packages/ui/src/pierre/commented-lines.ts


52. packages/app/src/utils/comment-note.ts ✨ Enhancement +88/-0

Add comment metadata utilities

• New utility module for managing code comments with metadata (path, selection, comment text,
 origin)
• Provides createCommentMetadata() and readCommentMetadata() for serialization/deserialization
• Includes formatCommentNote() to convert comments to human-readable format and
 parseCommentNote() to parse them back
• Validates file selections and supports review/file origin tracking

packages/app/src/utils/comment-note.ts


53. packages/app/src/i18n/th.ts 📝 Documentation +9/-7

Update Thai translations for permissions and providers

• Updated Thai translations for permission-related commands and toasts to use "สิทธิ์" (permissions)
 instead of "การแก้ไข" (edits)
• Added dialog.provider.opencode.tagline and dialog.provider.opencodeGo.tagline for provider
 descriptions
• Updated Copilot provider note to clarify it's for GitHub Copilot integration

packages/app/src/i18n/th.ts


54. packages/ui/src/pierre/file-runtime.ts ✨ Enhancement +114/-0

Add file viewer runtime utilities

• New module providing utilities for managing file viewer shadow DOM readiness and color scheme
 synchronization
• Exports createReadyWatcher(), clearReadyWatcher(), and notifyShadowReady() for monitoring
 viewer initialization
• Includes observeViewerScheme() to sync color scheme between document and viewer host
• Provides helper functions getViewerHost(), getViewerRoot(), and applyViewerScheme()

packages/ui/src/pierre/file-runtime.ts


55. packages/ui/src/pierre/file-selection.ts ✨ Enhancement +85/-0

Add file selection utilities for shadow DOM

• New module for handling file and diff line selection in shadow DOM contexts
• Exports findFileLineNumber(), findDiffLineNumber(), and findCodeSelectionSide() for line
 detection
• Provides readShadowLineSelection() to extract text selection ranges from shadow DOM with support
 for composed ranges
• Handles both unified and split diff views with side tracking (additions/deletions)

packages/ui/src/pierre/file-selection.ts


56. packages/app/src/pages/session/composer/session-composer-state.test.ts 🧪 Tests +83/-0

Add session request tree tests

• New test file for session request tree functions sessionPermissionRequest() and
 sessionQuestionRequest()
• Tests preference for current session requests over parent/child requests
• Verifies correct handling of nested session hierarchies and undefined cases

packages/app/src/pages/session/composer/session-composer-state.test.ts


57. packages/opencode/src/util/git.ts Refactoring +24/-53

Replace Bun.spawn with Process utility

• Replaced Bun.spawn() and $ shell with new Process.run() utility for consistent error
 handling
• Simplified git command execution by removing platform-specific ACP client logic
• Updated return type to use Buffer instead of ReadableStream for stdout/stderr
• Improved error handling with nothrow option and consistent error formatting

packages/opencode/src/util/git.ts


58. packages/app/e2e/files/file-viewer.spec.ts 🧪 Tests +57/-3

Update file viewer tests and add search test

• Updated selectors from [data-component="code"] to [data-component="file"][data-mode="text"]
 for file viewer
• Added new test cmd+f opens text viewer search while prompt is focused verifying search
 functionality
• Test opens file dialog, searches for package.json, and verifies search input focus in viewer

packages/app/e2e/files/file-viewer.spec.ts


59. packages/app/src/i18n/bs.ts 📝 Documentation +9/-7

Update Bosnian translations for permissions and providers

• Updated Bosnian translations for permission-related commands and toasts to use "dozvole"
 (permissions) instead of "izmjene" (edits)
• Added dialog.provider.opencode.tagline and dialog.provider.opencodeGo.tagline for provider
 descriptions
• Updated Copilot provider note to clarify GitHub Copilot integration

packages/app/src/i18n/bs.ts


60. packages/app/src/pages/layout/helpers.test.ts 🧪 Tests +75/-1

Add latest root session tests

• Added latestRootSession import and comprehensive tests for finding latest root session across
 workspaces
• Tests verify correct session selection based on update time and filtering of archived/child
 sessions
• Includes helper session() function to create test session objects with default values

packages/app/src/pages/layout/helpers.test.ts


61. script/beta.ts ✨ Enhancement +62/-13

Add automatic merge conflict resolution

• Added conflicts() function to detect unmerged files from git merge
• Added cleanup() function to abort merge and reset working tree
• Added fix() function to auto-resolve merge conflicts using opencode AI
• Updated merge error handling to attempt conflict resolution before failing

script/beta.ts


62. packages/ui/src/pierre/diff-selection.ts ✨ Enhancement +71/-0

Add diff selection utilities

• New module for handling diff line selection and validation in shadow DOM
• Exports findDiffSide() to determine if line is addition or deletion
• Provides diffLineIndex() and diffRowIndex() for mapping between line numbers and row indices
• Includes fixDiffSelection() to validate and correct reversed selection ranges

packages/ui/src/pierre/diff-selection.ts


63. packages/app/src/i18n/ja.ts 📝 Documentation +8/-6

Update Japanese translations for permissions and providers

• Updated Japanese translations for permission-related commands and toasts to use "権限" (permissions)
 instead of "編集" (edits)
• Added dialog.provider.opencode.tagline and dialog.provider.opencodeGo.tagline for provider
 descriptions
• Updated Copilot provider note to clarify GitHub Copilot integration

packages/app/src/i18n/ja.ts


64. packages/app/src/i18n/ar.ts 📦 Other +8/-6
• Updated Arabic translations for permission-related commands and toasts to use "الأذونات"
 (permissions) instead of "التعديلات" (edits)
• Added dialog.provider.opencode.tagline and dialog.provider.opencodeGo.tagline for provider
 descriptions
• Updated Copilot provider

packages/app/src/i18n/ar.ts


65. .github/actions/setup-bun/action.yml Additional files +16/-1

...

.github/actions/setup-bun/action.yml


66. .github/workflows/beta.yml Additional files +4/-0

...

.github/workflows/beta.yml


67. .github/workflows/compliance-close.yml Additional files +9/-0

...

.github/workflows/compliance-close.yml


68. .github/workflows/docs-locale-sync.yml Additional files +4/-4

...

.github/workflows/docs-locale-sync.yml


69. .github/workflows/pr-standards.yml Additional files +6/-6

...

.github/workflows/pr-standards.yml


70. .github/workflows/test.yml Additional files +10/-2

...

.github/workflows/test.yml


71. .github/workflows/vouch-check-issue.yml Additional files +39/-19

...

.github/workflows/vouch-check-issue.yml


72. .github/workflows/vouch-check-pr.yml Additional files +38/-17

...

.github/workflows/vouch-check-pr.yml


73. .github/workflows/vouch-manage-by-issue.yml Additional files +1/-0

...

.github/workflows/vouch-manage-by-issue.yml


74. .opencode/agent/translator.md Additional files +1/-1

...

.opencode/agent/translator.md


75. .opencode/glossary/README.md Additional files +0/-0

...

.opencode/glossary/README.md


76. .opencode/glossary/ar.md Additional files +0/-0

...

.opencode/glossary/ar.md


77. .opencode/glossary/br.md Additional files +0/-0

...

.opencode/glossary/br.md


78. .opencode/glossary/bs.md Additional files +0/-0

...

.opencode/glossary/bs.md


79. .opencode/glossary/da.md Additional files +0/-0

...

.opencode/glossary/da.md


80. .opencode/glossary/de.md Additional files +0/-0

...

.opencode/glossary/de.md


81. .opencode/glossary/es.md Additional files +0/-0

...

.opencode/glossary/es.md


82. .opencode/glossary/fr.md Additional files +0/-0

...

.opencode/glossary/fr.md


83. .opencode/glossary/ja.md Additional files +0/-0

...

.opencode/glossary/ja.md


84. .opencode/glossary/ko.md Additional files +0/-0

...

.opencode/glossary/ko.md


85. .opencode/glossary/no.md Additional files +0/-0

...

.opencode/glossary/no.md


86. .opencode/glossary/pl.md Additional files +0/-0

...

.opencode/glossary/pl.md


87. .opencode/glossary/ru.md Additional files +0/-0

...

.opencode/glossary/ru.md


88. .opencode/glossary/th.md Additional files +0/-0

...

.opencode/glossary/th.md


89. .opencode/glossary/zh-cn.md Additional files +0/-0

...

.opencode/glossary/zh-cn.md


90. .opencode/glossary/zh-tw.md Additional files +0/-0

...

.opencode/glossary/zh-tw.md


91. nix/hashes.json Additional files +4/-4

...

nix/hashes.json


92. package.json Additional files +1/-1

...

package.json


93. packages/app/e2e/files/file-tree.spec.ts Additional files +3/-3

...

packages/app/e2e/files/file-tree.spec.ts


94. packages/app/e2e/utils.ts Additional files +1/-1

...

packages/app/e2e/utils.ts


95. packages/app/package.json Additional files +1/-1

...

packages/app/package.json


96. packages/app/playwright.config.ts Additional files +2/-2

...

packages/app/playwright.config.ts
...

@qodo-free-for-open-source-projects
Copy link

qodo-free-for-open-source-projects bot commented Feb 27, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (9) 📎 Requirement gaps (0)

Grey Divider


Action required

1. cleanup() swallows command errors 📘 Rule violation ⛯ Reliability
Description
conflicts() and cleanup() intentionally ignore failures via empty catch {} / `.catch(() =>
"")`, which can hide critical merge/cleanup failures and make incident debugging difficult. This
violates the requirement to handle failure points with meaningful context and avoid unnecessary
try/catch usage.
Code

script/beta.ts[R33-50]

+async function conflicts() {
+  const out = await $`git diff --name-only --diff-filter=U`.text().catch(() => "")
+  return out
+    .split("\n")
+    .map((x) => x.trim())
+    .filter(Boolean)
+}
+
+async function cleanup() {
+  try {
+    await $`git merge --abort`
+  } catch {}
+  try {
+    await $`git checkout -- .`
+  } catch {}
+  try {
+    await $`git clean -fd`
+  } catch {}
Evidence
PR Compliance ID 3 requires failure points not be silently ignored, and PR Compliance ID 8
discourages unnecessary/broad try/catch—here failures are explicitly swallowed without logging or
actionable context.

Rule 3: Generic: Robust Error Handling and Edge Case Management
AGENTS.md
script/beta.ts[33-50]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`script/beta.ts` currently swallows errors from git commands (empty `catch {}`) and suppresses errors when reading conflicts (`.catch(() =&gt; &quot;&quot;)`). This can hide real failures and makes diagnosing merge/cleanup issues difficult.

## Issue Context
The compliance checklist requires robust error handling with actionable context, and discourages broad/unnecessary try/catch blocks.

## Fix Focus Areas
- script/beta.ts[33-50]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. field cast to any 📘 Rule violation ✓ Correctness
Description
The streaming update path uses event.properties.field as any when calling setStore, erasing type
safety for store writes and risking runtime updates to invalid/non-string fields. This introduces
new any usage in a hot-path update.
Code

packages/opencode/src/cli/cmd/tui/context/sync.tsx[R307-313]

          setStore(
            "part",
            event.properties.messageID,
-            produce((draft) => {
-              const part = draft[result.index]
-              const field = event.properties.field as keyof typeof part
-              const existing = part[field] as string | undefined
-              ;(part[field] as string) = (existing ?? "") + event.properties.delta
-            }),
+            result.index,
+            event.properties.field as any,
+            (prev: string) => (prev ?? "") + event.properties.delta,
          )
Evidence
PR Compliance ID 9 forbids introducing any; the new code explicitly casts event.properties.field
to any to satisfy setStore typing.

AGENTS.md
packages/opencode/src/cli/cmd/tui/context/sync.tsx[307-313]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`event.properties.field` is cast to `any` when calling `setStore(...)`, which defeats type checking for store updates.

## Issue Context
This is on the streaming hot path (`message.part.delta`). The field appears to be intended as a key into a message-part object, and the update function assumes the field holds a string.

## Fix Focus Areas
- packages/opencode/src/cli/cmd/tui/context/sync.tsx[307-313]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. POST body typed any 📘 Rule violation ✓ Correctness
Description
The new POST handler parses request body as any, which removes compile-time guarantees for
external input handling and can mask missing validation/narrowing. This introduces any into
request parsing code.
Code

packages/console/app/src/routes/zen/go/v1/messages.ts[R8-10]

+    parseApiKey: (headers: Headers) => headers.get("x-api-key") ?? undefined,
+    parseModel: (url: string, body: any) => body.model,
+    parseIsStream: (url: string, body: any) => !!body.stream,
Evidence
PR Compliance ID 9 requires avoiding any; the route explicitly annotates the request body as any
in parsing callbacks.

AGENTS.md
packages/console/app/src/routes/zen/go/v1/messages.ts[8-10]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The request `body` is typed as `any` in `parseModel`/`parseIsStream`, which erases type safety for external inputs.

## Issue Context
This is a new API route handler; input bodies should be typed safely (e.g., `unknown` + narrowing) rather than `any`.

## Fix Focus Areas
- packages/console/app/src/routes/zen/go/v1/messages.ts[8-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (8)
4. Diagnostics typed with any 📘 Rule violation ✓ Correctness
Description
Diagnostics introduces any in its diagnostics prop type (`Record<string, Record<string,
any>[]>`), undermining type safety for LSP diagnostic rendering and making misuse easy. This
violates the no-any requirement in new code.
Code

packages/opencode/src/cli/cmd/tui/routes/session/index.tsx[2141]

+function Diagnostics(props: { diagnostics?: Record<string, Record<string, any>[]>; filePath: string }) {
Evidence
PR Compliance ID 9 forbids new any usage; the new Diagnostics function signature includes any
inside the diagnostics type.

AGENTS.md
packages/opencode/src/cli/cmd/tui/routes/session/index.tsx[2141-2141]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`Diagnostics` uses `any` in its prop typing, which removes type safety for diagnostic rendering.

## Issue Context
The component reads `severity`, `range.start.line`, `range.start.character`, and `message`, so the type can be specified structurally or by importing an existing diagnostic type.

## Fix Focus Areas
- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx[2141-2147]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. loadFile returns any 📘 Rule violation ✓ Correctness
Description
createOpenReviewFile changes loadFile to return any | Promise<void>, introducing any where
void | Promise<void> (or a typed result) would suffice. This reduces type safety and violates the
no-any rule.
Code

packages/app/src/pages/session/helpers.ts[R24-35]

  showAllFiles: () => void
  tabForPath: (path: string) => string
  openTab: (tab: string) => void
-  loadFile: (path: string) => void
+  loadFile: (path: string) => any | Promise<void>
}) => {
  return (path: string) => {
    batch(() => {
      input.showAllFiles()
-      input.openTab(input.tabForPath(path))
-      input.loadFile(path)
+      const maybePromise = input.loadFile(path)
+      const openTab = () => input.openTab(input.tabForPath(path))
+      if (maybePromise instanceof Promise) maybePromise.then(openTab)
+      else openTab()
Evidence
PR Compliance ID 9 forbids introducing any; the modified function type explicitly returns any.

AGENTS.md
packages/app/src/pages/session/helpers.ts[23-35]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`loadFile` is typed as returning `any | Promise&lt;void&gt;`, introducing `any` unnecessarily.

## Issue Context
The implementation only needs to distinguish between synchronous `void` and async `Promise&lt;void&gt;`.

## Fix Focus Areas
- packages/app/src/pages/session/helpers.ts[23-35]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. FileMedia uses as any 📘 Rule violation ✓ Correctness
Description
FileMedia repeatedly casts unknown media values to any, bypassing type checks and potentially
passing invalid values into media helpers. This introduces multiple any casts in a new component.
Code

packages/ui/src/components/file-media.tsx[R39-55]

+  const isBinary = createMemo(() => {
+    const media = cfg()
+    if (!media || media.mode === "off") return false
+    if (kind()) return false
+    return isBinaryContent(media.current as any)
+  })
+
+  const onLoad = () => props.media?.onLoad?.()
+
+  const deleted = createMemo(() => {
+    const media = cfg()
+    const k = kind()
+    if (!media || !k) return false
+    if (k === "svg") return false
+    if (media.current !== undefined) return false
+    return !hasMediaValue(media.after as any) && hasMediaValue(media.before as any)
+  })
Evidence
PR Compliance ID 9 forbids new any usage; the new component uses as any for media.current,
media.after, and media.before.

AGENTS.md
packages/ui/src/components/file-media.tsx[39-55]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`packages/ui/src/components/file-media.tsx` uses `as any` casts on `unknown` media values, which defeats type safety.

## Issue Context
The component already models `current`/`before`/`after` as `unknown`; the callers should narrow these values (or the helper functions should accept `unknown`) instead of casting to `any`.

## Fix Focus Areas
- packages/ui/src/components/file-media.tsx[39-55]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. LineComment handlers cast any 📘 Rule violation ✓ Correctness
Description
LineCommentAnchor casts event handlers to any for on:click, on:mouseenter, and
on:focusout, hiding potential mismatches between handler signatures and actual events. This
introduces any into UI event wiring.
Code

packages/ui/src/components/line-comment.tsx[R78-118]

+            <button
+              type="button"
+              aria-label={props.buttonLabel}
+              data-slot="line-comment-button"
+              on:mousedown={(e) => e.stopPropagation()}
+              on:mouseup={(e) => e.stopPropagation()}
+              on:click={props.onClick as any}
+              on:mouseenter={props.onMouseEnter as any}
+            >
+              <Show
+                when={props.inline}
+                fallback={<Icon name={icon() === "plus" ? "plus-small" : "comment"} size="small" />}
+              >
+                <InlineGlyph icon={icon()} />
+              </Show>
+            </button>
+            <Show when={props.open}>
+              <div
+                data-slot="line-comment-popover"
+                classList={{
+                  [props.popoverClass ?? ""]: !!props.popoverClass,
+                }}
+                on:mousedown={(e) => e.stopPropagation()}
+                on:focusout={props.onPopoverFocusOut as any}
+              >
+                {props.children}
+              </div>
+            </Show>
+          </>
+        }
+      >
        <div
          data-slot="line-comment-popover"
+          data-inline-body=""
          classList={{
            [props.popoverClass ?? ""]: !!props.popoverClass,
          }}
-          onFocusOut={props.onPopoverFocusOut}
+          on:mousedown={(e) => e.stopPropagation()}
+          on:click={props.onClick as any}
+          on:mouseenter={props.onMouseEnter as any}
+          on:focusout={props.onPopoverFocusOut as any}
Evidence
PR Compliance ID 9 forbids introducing any; multiple event handler props are explicitly cast to
any in the modified component.

AGENTS.md
packages/ui/src/components/line-comment.tsx[78-118]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`line-comment.tsx` uses `as any` casts for event handlers, removing type safety for UI event wiring.

## Issue Context
The component already defines handler props; they should be passed without `any` by ensuring the prop types match the events used (or by adapting via small wrapper functions).

## Fix Focus Areas
- packages/ui/src/components/line-comment.tsx[78-118]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Highlights types use any 📘 Rule violation ✓ Correctness
Description
setHighlights() uses any in global typing for CSS.highlights and Highlight, which disables
type checking for a browser API integration. This introduces avoidable any into new logic.
Code

packages/ui/src/pierre/file-find.ts[R334-336]

+  const setHighlights = (ranges: FileFindHit[], currentIndex: number) => {
+    const api = (globalThis as unknown as { CSS?: { highlights?: any }; Highlight?: any }).CSS?.highlights
+    const Highlight = (globalThis as unknown as { Highlight?: any }).Highlight
Evidence
PR Compliance ID 9 forbids introducing any; the new highlight integration uses highlights?: any
and Highlight?: any in type assertions.

AGENTS.md
packages/ui/src/pierre/file-find.ts[334-336]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The highlight API integration uses `any` in global type assertions, defeating type checking.

## Issue Context
Only a small surface is needed (`set`, `delete`, and `new Highlight(...)`); define minimal interfaces/types and keep runtime feature detection.

## Fix Focus Areas
- packages/ui/src/pierre/file-find.ts[334-336]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Test casts newSession args 📘 Rule violation ✓ Correctness
Description
The new ACP event-subscription tests cast agent.newSession(...) arguments to any, weakening type
safety and potentially hiding incorrect test setup. This introduces new any usage in tests.
Code

packages/opencode/test/acp/event-subscription.test.ts[R496-504]

+  test("streams running bash output snapshots and de-dupes identical snapshots", async () => {
+    await using tmp = await tmpdir()
+    await Instance.provide({
+      directory: tmp.path,
+      fn: async () => {
+        const { agent, controller, sessionUpdates, stop } = createFakeAgent()
+        const cwd = "/tmp/opencode-acp-test"
+        const sessionId = await agent.newSession({ cwd, mcpServers: [] } as any).then((x) => x.sessionId)
+        const input = { command: "echo hello", description: "run command" }
Evidence
PR Compliance ID 9 forbids introducing any; the test explicitly uses as any when calling
agent.newSession(...).

AGENTS.md
packages/opencode/test/acp/event-subscription.test.ts[496-504]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The test calls `agent.newSession(...)` with an argument cast to `any`, which bypasses type checking.

## Issue Context
Tests should still preserve type safety; prefer `satisfies` or importing the correct input type for `newSession`.

## Fix Focus Areas
- packages/opencode/test/acp/event-subscription.test.ts[496-504]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Lite webhook unique constraint loop 🐞 Bug ⛯ Reliability
Description
customer.subscription.created (lite) unconditionally inserts into LiteTable, which has a unique
index on (workspaceID,userID). Any Stripe retry/duplicate delivery will hit a unique constraint
error and return 500, causing repeated retries and leaving the workspace in a stuck/partial billing
state.
Code

packages/console/app/src/routes/stripe/webhook.ts[R150-168]

+          await Database.transaction(async (tx) => {
+            await tx
+              .update(BillingTable)
+              .set({
+                customerID,
+                liteSubscriptionID: subscriptionID,
+                lite: {},
+                paymentMethodID: paymentMethod.id,
+                paymentMethodLast4: paymentMethod.card?.last4 ?? null,
+                paymentMethodType: paymentMethod.type,
+              })
+              .where(eq(BillingTable.workspaceID, workspaceID))

-          await tx.insert(PaymentTable).values({
-            workspaceID,
-            id: Identifier.create("payment"),
-            amount: centsToMicroCents(amountInCents),
-            paymentID,
-            invoiceID,
-            customerID,
-            enrichment: {
-              type: "subscription",
-              couponID,
-            },
+            await tx.insert(LiteTable).values({
+              workspaceID,
+              id: Identifier.create("lite"),
+              userID: userID,
+            })
          })
Evidence
The webhook handler always inserts a new Lite row for the workspace/user pair, but the DB schema
enforces uniqueness on that pair, so a second delivery will fail deterministically.

packages/console/app/src/routes/stripe/webhook.ts[150-168]
packages/console/core/src/schema/billing.sql.ts[62-76]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`customer.subscription.created` for `type === &quot;lite&quot;` inserts into `LiteTable` unconditionally, but `LiteTable` enforces a unique index on `(workspaceID, userID)`. Stripe webhooks are at-least-once; duplicates/retries will cause a constraint error and repeated 500 responses.

### Issue Context
The handler should be idempotent: if the Lite row already exists for the workspace/user, the webhook should treat it as success.

### Fix Focus Areas
- packages/console/app/src/routes/stripe/webhook.ts[150-168]
- packages/console/core/src/schema/billing.sql.ts[62-76]

### Implementation notes
- Prefer a DB-level upsert (e.g., `onDuplicateKeyUpdate` / equivalent) for `LiteTable` keyed by `(workspaceID, userID)`.
- Alternatively, `select` then `insert` only if absent, but ensure it’s race-safe.
- Consider also persisting a Stripe event id (or subscription id) to dedupe across retries.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Lite webhook requires payment intent 🐞 Bug ✓ Correctness
Description
The Lite customer.subscription.created handler throws if the latest invoice has no payment
intent/payment method. The same webhook file explicitly acknowledges payment intents can be
undefined when coupons are used, so Lite subscriptions with discounts/trials can fail with 500 and
never provision Lite access.
Code

packages/console/app/src/routes/stripe/webhook.ts[R121-134]

+        // get payment id from invoice
+        const invoice = await Billing.stripe().invoices.retrieve(invoiceID, {
+          expand: ["payments"],
+        })
+        const paymentID = invoice.payments?.data[0].payment.payment_intent as string
+        if (!paymentID) throw new Error("Payment ID not found")

-        // set customer metadata
-        if (!billing?.customerID) {
-          await Billing.stripe().customers.update(customerID, {
-            metadata: {
-              workspaceID,
-            },
-          })
-        }
+        // get payment method for the payment intent
+        const paymentIntent = await Billing.stripe().paymentIntents.retrieve(paymentID, {
+          expand: ["payment_method"],
+        })
+        const paymentMethod = paymentIntent.payment_method
+        if (!paymentMethod || typeof paymentMethod === "string") throw new Error("Payment method not expanded")

-        await Database.transaction(async (tx) => {
-          await tx
-            .update(BillingTable)
-            .set({
-              customerID,
-              subscriptionID,
-              subscription: {
-                status: "subscribed",
-                coupon: couponID,
-                seats: 1,
-                plan: "200",
Evidence
In the lite subscription-created branch, paymentID is required and missing values hard-error.
Elsewhere in the same handler, the code comments that paymentID can legitimately be undefined with
coupons, demonstrating this is an expected real-world case that the lite path doesn’t handle.

packages/console/app/src/routes/stripe/webhook.ts[121-134]
packages/console/app/src/routes/stripe/webhook.ts[218-226]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The Lite `customer.subscription.created` handler assumes `latest_invoice` always has a payment intent and expanded payment method. For coupon/trial scenarios, Stripe may not create a payment intent, and the code will throw, returning 500.

### Issue Context
The same webhook already documents that `paymentID` can be undefined with coupons in another path; Lite should follow similar robustness.

### Fix Focus Areas
- packages/console/app/src/routes/stripe/webhook.ts[121-134]
- packages/console/app/src/routes/stripe/webhook.ts[218-226]

### Implementation notes
- If `paymentID` is missing, proceed with Lite provisioning and skip updating `paymentMethod*` fields (or fetch default payment method from `customer.invoice_settings.default_payment_method` / subscription defaults).
- Ensure the handler still returns 2xx when state is consistent, to avoid Stripe retry storms.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

12. TUI bootstrap may keep stale state 🐞 Bug ✓ Correctness
Description
In TUI sync.tsx, reconcile() was removed and replaced with raw setStore() for bootstrap-loaded
objects (config, provider lists, auth maps). Because bootstrap can run again on
server.instance.disposed and upstream config is assembled as a sparse object (starting from {}),
missing keys on reload can leave stale entries in the store if setStore() merges rather than fully
replaces/clears.
Code

packages/opencode/src/cli/cmd/tui/context/sync.tsx[R387-413]

            batch(() => {
-              setStore("provider", reconcile(providers.providers))
-              setStore("provider_default", reconcile(providers.default))
-              setStore("provider_next", reconcile(providerList))
-              setStore("agent", reconcile(agents))
-              setStore("config", reconcile(config))
-              if (sessions !== undefined) setStore("session", reconcile(sessions))
+              setStore("provider", providers.providers)
+              setStore("provider_default", providers.default)
+              setStore("provider_next", providerList)
+              setStore("agent", agents)
+              setStore("config", config)
+              if (sessions !== undefined) setStore("session", sessions)
            })
          })
        })
        .then(() => {
          if (store.status !== "complete") setStore("status", "partial")
          // non-blocking
          Promise.all([
-            ...(args.continue ? [] : [sessionListPromise.then((sessions) => setStore("session", reconcile(sessions)))]),
-            sdk.client.command.list().then((x) => setStore("command", reconcile(x.data ?? []))),
-            sdk.client.lsp.status().then((x) => setStore("lsp", reconcile(x.data!))),
-            sdk.client.mcp.status().then((x) => setStore("mcp", reconcile(x.data!))),
-            sdk.client.experimental.resource.list().then((x) => setStore("mcp_resource", reconcile(x.data ?? {}))),
-            sdk.client.formatter.status().then((x) => setStore("formatter", reconcile(x.data!))),
+            ...(args.continue ? [] : [sessionListPromise.then((sessions) => setStore("session", sessions))]),
+            sdk.client.command.list().then((x) => setStore("command", x.data ?? [])),
+            sdk.client.lsp.status().then((x) => setStore("lsp", x.data!)),
+            sdk.client.mcp.status().then((x) => setStore("mcp", x.data!)),
+            sdk.client.experimental.resource.list().then((x) => setStore("mcp_resource", x.data ?? {})),
+            sdk.client.formatter.status().then((x) => setStore("formatter", x.data!)),
            sdk.client.session.status().then((x) => {
-              setStore("session_status", reconcile(x.data!))
+              setStore("session_status", x.data!)
            }),
-            sdk.client.provider.auth().then((x) => setStore("provider_auth", reconcile(x.data ?? {}))),
-            sdk.client.vcs.get().then((x) => setStore("vcs", reconcile(x.data))),
-            sdk.client.path.get().then((x) => setStore("path", reconcile(x.data!))),
+            sdk.client.provider.auth().then((x) => setStore("provider_auth", x.data ?? {})),
+            sdk.client.vcs.get().then((x) => setStore("vcs", x.data)),
+            sdk.client.path.get().then((x) => setStore("path", x.data!)),
          ]).then(() => {
Evidence
The code explicitly re-runs bootstrap on server disposal, and bootstrap updates several complex
objects via setStore. Config assembly starts from an empty object and merges sources, implying the
resulting object can omit keys. Separately, other code in the repo uses reconcile({}) specifically
to clear objects, indicating that naive setStore updates may not clear prior keys in some store
shapes.

packages/opencode/src/cli/cmd/tui/context/sync.tsx[107-112]
packages/opencode/src/cli/cmd/tui/context/sync.tsx[387-413]
packages/opencode/src/config/config.ts[86-105]
packages/app/src/context/settings.tsx[180-182]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
After removing `reconcile()` in TUI `sync.tsx`, bootstrap now uses raw `setStore()` to apply server snapshots (config/provider/auth/resources). Bootstrap can re-run on `server.instance.disposed`. Config assembly starts from `{}` and merges sources, meaning absent keys are a normal output; the store refresh path should clear removed keys rather than potentially retaining stale state.

### Issue Context
The performance problem described in the PR is driven by very high-frequency streaming deltas; bootstrap and reconnect flows are not hot-path and can safely use more thorough replacement semantics.

### Fix Focus Areas
- packages/opencode/src/cli/cmd/tui/context/sync.tsx[387-413]
- packages/opencode/src/cli/cmd/tui/context/sync.tsx[107-112]

### Implementation notes
- Consider reintroducing `reconcile()` only for bootstrap snapshot replacements (config/provider/provider_auth/mcp_resource), while keeping path-syntax `setStore` for streaming deltas.
- If avoiding `reconcile()` entirely is a goal, explicitly clear old map keys before assigning new objects (e.g., via `produce` deleting existing keys then `Object.assign`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@coleleavitt
Copy link
Owner Author

Closing — fork's dev branch was stale when PR was created, causing 104 commits in diff. Recreating with synced fork.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

15 issues found across 409 files

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/compliance-close.yml">

<violation number="1" location=".github/workflows/compliance-close.yml:75">
P2: Empty `catch` block silently swallows all errors, including unexpected ones (network failures, auth errors, rate limits). Consider logging the error with `core.warning` to preserve observability, matching the workflow's existing logging pattern with `core.info`.</violation>
</file>

<file name="packages/app/src/pages/session/review-tab.tsx">

<violation number="1" location="packages/app/src/pages/session/review-tab.tsx:136">
P1: Event listeners are never actually removed: `addEventListener` uses `{ capture: true }` but `removeEventListener` omits it (defaults to `false`). The `capture` flag must match for removal to work (per MDN spec). These four listeners will leak on every cleanup.</violation>
</file>

<file name="packages/app/src/context/permission.tsx">

<violation number="1" location="packages/app/src/context/permission.tsx:67">
P3: Migration leaves stale `autoAcceptEdits` key in persisted data. The spread `...data` copies all old keys, and the `merge()` function in `persist.ts` preserves extra keys not in defaults. Consider destructuring out the old key to keep persisted data clean.</violation>
</file>

<file name="packages/app/src/components/dialog-select-model-unpaid.tsx">

<violation number="1" location="packages/app/src/components/dialog-select-model-unpaid.tsx:100">
P2: Duplicate `<Show when={i.id === "opencode"}>` condition — merge into a single `<Show>` block with a fragment, consistent with the `"opencode-go"` pattern added in this same diff.</violation>
</file>

<file name="packages/app/src/context/layout-scroll.ts">

<violation number="1" location="packages/app/src/context/layout-scroll.ts:36">
P2: Unnecessary `clone()` on the hot path: `clone(opts.getSnapshot(sessionKey))` is called before the early-return check for a non-empty `current`. Move the clone after the early-return to avoid allocating an object that gets immediately discarded on every scroll event.</violation>
</file>

<file name="packages/app/src/context/file/path.ts">

<violation number="1" location="packages/app/src/context/file/path.ts:108">
P2: `normalizeDir` only strips trailing forward slashes (`/\/+$/`), but `normalize` now preserves native backslashes on Windows. A Windows directory path with a trailing backslash (e.g. `C:\\repo\\subdir\\`) will normalize to `"subdir\\"`, and `normalizeDir` won't strip the trailing `\`. Consider updating the regex to strip both separators: `/[\/\\]+$/`.</violation>
</file>

<file name="packages/app/src/components/settings-providers.tsx">

<violation number="1" location="packages/app/src/components/settings-providers.tsx:190">
P2: Two consecutive `<Show when={item.id === "opencode"}>` blocks evaluate the same condition. Merge them into a single `<Show>` with a fragment, consistent with how the `opencode-go` case is handled just below.</violation>
</file>

<file name="packages/app/src/pages/session/helpers.ts">

<violation number="1" location="packages/app/src/pages/session/helpers.ts:34">
P2: Missing `.catch()` on the promise chain — if `loadFile` rejects, this produces an unhandled promise rejection.</violation>
</file>

<file name=".github/actions/setup-bun/action.yml">

<violation number="1" location=".github/actions/setup-bun/action.yml:20">
P2: The `case` statement has no default (`*`) branch. If `RUNNER_OS` doesn't match any listed value, `$OS` is unset and the URL output will be malformed (e.g. `bun--x64-baseline.zip`), causing a hard-to-debug 404 in the download step. Add a fallback or guard to avoid silently producing a broken URL.</violation>
</file>

<file name="packages/app/src/context/prompt.tsx">

<violation number="1" location="packages/app/src/context/prompt.tsx:120">
P2: `isCommentItem` misses items that have a `commentID` but no `comment` text. This means `replaceComments` can leave stale comment items in the list when they lack a trimmed `comment` string, since it only filters items where `!!item.comment?.trim()` is true. The check should also account for `commentID`.</violation>
</file>

<file name="packages/console/app/src/i18n/fr.ts">

<violation number="1" location="packages/console/app/src/i18n/fr.ts:501">
P2: Missing English source translations: the `workspace.lite.*` keys are added here in `fr.ts` but have no corresponding entries in `en.ts`. This will cause the i18n fallback to show raw key strings (e.g., `workspace.lite.subscription.title`) for English users, or for any locale that falls back to English.</violation>
</file>

<file name="packages/app/src/pages/layout.tsx">

<violation number="1" location="packages/app/src/pages/layout.tsx:1097">
P2: Making `navigateToProject` async without updating callers risks unhandled promise rejections. None of the 6+ call sites `await` or `.catch()` the returned promise. Wrap the body in a top-level `try/catch` so the function never rejects when called fire-and-forget.</violation>
</file>

<file name="packages/app/src/utils/server-errors.ts">

<violation number="1" location="packages/app/src/utils/server-errors.ts:23">
P2: Typo in exported function name: `parseReabaleConfigInvalidError` → `parseReadableConfigInvalidError`. Since this is a new public API, fixing the name now avoids a breaking rename later.</violation>

<violation number="2" location="packages/app/src/utils/server-errors.ts:30">
P2: The empty string `""` in the array is always removed by `filter(Boolean)`, so it has no effect. If a blank line separator between the file and issues was intended, this is a bug — you'd need to filter first, then insert the separator. If no separator was intended, remove the `""` to avoid confusion.</violation>
</file>

<file name="packages/app/src/pages/session/session-side-panel.tsx">

<violation number="1" location="packages/app/src/pages/session/session-side-panel.tsx:240">
P2: Review count badge lost all styling classes (typography, color, padding, rounded pill background). This appears to be an unintentional UI regression — the count will render as plain unstyled text instead of the previous pill-shaped badge.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment on lines +136 to +139
scroll.removeEventListener("wheel", handleInteraction)
scroll.removeEventListener("pointerdown", handleInteraction)
scroll.removeEventListener("touchstart", handleInteraction)
scroll.removeEventListener("keydown", handleInteraction)
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

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

P1: Event listeners are never actually removed: addEventListener uses { capture: true } but removeEventListener omits it (defaults to false). The capture flag must match for removal to work (per MDN spec). These four listeners will leak on every cleanup.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app/src/pages/session/review-tab.tsx, line 136:

<comment>Event listeners are never actually removed: `addEventListener` uses `{ capture: true }` but `removeEventListener` omits it (defaults to `false`). The `capture` flag must match for removal to work (per MDN spec). These four listeners will leak on every cleanup.</comment>

<file context>
@@ -85,48 +63,81 @@ export function SessionReviewTab(props: SessionReviewTabProps) {
-    cancelAnimationFrame(frame)
+    if (restoreFrame !== undefined) cancelAnimationFrame(restoreFrame)
+    if (scroll) {
+      scroll.removeEventListener("wheel", handleInteraction)
+      scroll.removeEventListener("pointerdown", handleInteraction)
+      scroll.removeEventListener("touchstart", handleInteraction)
</file context>
Suggested change
scroll.removeEventListener("wheel", handleInteraction)
scroll.removeEventListener("pointerdown", handleInteraction)
scroll.removeEventListener("touchstart", handleInteraction)
scroll.removeEventListener("keydown", handleInteraction)
scroll.removeEventListener("wheel", handleInteraction, { capture: true })
scroll.removeEventListener("pointerdown", handleInteraction, { capture: true })
scroll.removeEventListener("touchstart", handleInteraction, { capture: true })
scroll.removeEventListener("keydown", handleInteraction, { capture: true })
Fix with Cubic

issue_number: item.number,
name: 'needs:compliance',
});
} catch (e) {}
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

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

P2: Empty catch block silently swallows all errors, including unexpected ones (network failures, auth errors, rate limits). Consider logging the error with core.warning to preserve observability, matching the workflow's existing logging pattern with core.info.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/compliance-close.yml, line 75:

<comment>Empty `catch` block silently swallows all errors, including unexpected ones (network failures, auth errors, rate limits). Consider logging the error with `core.warning` to preserve observability, matching the workflow's existing logging pattern with `core.info`.</comment>

<file context>
@@ -65,6 +65,15 @@ jobs:
+                  issue_number: item.number,
+                  name: 'needs:compliance',
+                });
+              } catch (e) {}
+
               if (isPR) {
</file context>
Suggested change
} catch (e) {}
} catch (e) {
core.warning(`Failed to remove needs:compliance label from #${item.number}: ${e.message}`);
}
Fix with Cubic

<div class="w-full flex items-center gap-x-3">
<ProviderIcon data-slot="list-item-extra-icon" id={i.id as IconName} />
<span>{i.name}</span>
<Show when={i.id === "opencode"}>
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

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

P2: Duplicate <Show when={i.id === "opencode"}> condition — merge into a single <Show> block with a fragment, consistent with the "opencode-go" pattern added in this same diff.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app/src/components/dialog-select-model-unpaid.tsx, line 100:

<comment>Duplicate `<Show when={i.id === "opencode"}>` condition — merge into a single `<Show>` block with a fragment, consistent with the `"opencode-go"` pattern added in this same diff.</comment>

<file context>
@@ -97,9 +97,20 @@ export const DialogSelectModelUnpaid: Component = () => {
                   <div class="w-full flex items-center gap-x-3">
                     <ProviderIcon data-slot="list-item-extra-icon" id={i.id as IconName} />
                     <span>{i.name}</span>
+                    <Show when={i.id === "opencode"}>
+                      <div class="text-14-regular text-text-weak">{language.t("dialog.provider.opencode.tagline")}</div>
+                    </Show>
</file context>
Fix with Cubic

Comment on lines +36 to +45
const next = clone(opts.getSnapshot(sessionKey))
const current = cache[sessionKey]
if (!current) {
setCache(sessionKey, next)
return
}

if (Object.keys(current).length > 0) return
if (Object.keys(next).length === 0) return
setCache(sessionKey, next)
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

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

P2: Unnecessary clone() on the hot path: clone(opts.getSnapshot(sessionKey)) is called before the early-return check for a non-empty current. Move the clone after the early-return to avoid allocating an object that gets immediately discarded on every scroll event.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app/src/context/layout-scroll.ts, line 36:

<comment>Unnecessary `clone()` on the hot path: `clone(opts.getSnapshot(sessionKey))` is called before the early-return check for a non-empty `current`. Move the clone after the early-return to avoid allocating an object that gets immediately discarded on every scroll event.</comment>

<file context>
@@ -33,8 +33,16 @@ export function createScrollPersistence(opts: Options) {
   function seed(sessionKey: string) {
-    if (cache[sessionKey]) return
-    setCache(sessionKey, clone(opts.getSnapshot(sessionKey)))
+    const next = clone(opts.getSnapshot(sessionKey))
+    const current = cache[sessionKey]
+    if (!current) {
</file context>
Suggested change
const next = clone(opts.getSnapshot(sessionKey))
const current = cache[sessionKey]
if (!current) {
setCache(sessionKey, next)
return
}
if (Object.keys(current).length > 0) return
if (Object.keys(next).length === 0) return
setCache(sessionKey, next)
const current = cache[sessionKey]
if (current && Object.keys(current).length > 0) return
const next = clone(opts.getSnapshot(sessionKey))
if (!current) {
setCache(sessionKey, next)
return
}
if (Object.keys(next).length === 0) return
setCache(sessionKey, next)
Fix with Cubic

const root = scope()

let path = unquoteGitPath(decodeFilePath(stripQueryAndHash(stripFileProtocol(input)))).replace(/\\/g, "/")
let path = unquoteGitPath(decodeFilePath(stripQueryAndHash(stripFileProtocol(input))))
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

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

P2: normalizeDir only strips trailing forward slashes (/\/+$/), but normalize now preserves native backslashes on Windows. A Windows directory path with a trailing backslash (e.g. C:\\repo\\subdir\\) will normalize to "subdir\\", and normalizeDir won't strip the trailing \. Consider updating the regex to strip both separators: /[\/\\]+$/.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app/src/context/file/path.ts, line 108:

<comment>`normalizeDir` only strips trailing forward slashes (`/\/+$/`), but `normalize` now preserves native backslashes on Windows. A Windows directory path with a trailing backslash (e.g. `C:\\repo\\subdir\\`) will normalize to `"subdir\\"`, and `normalizeDir` won't strip the trailing `\`. Consider updating the regex to strip both separators: `/[\/\\]+$/`.</comment>

<file context>
@@ -103,32 +103,30 @@ export function encodeFilePath(filepath: string): string {
+    const root = scope()
 
-    let path = unquoteGitPath(decodeFilePath(stripQueryAndHash(stripFileProtocol(input)))).replace(/\\/g, "/")
+    let path = unquoteGitPath(decodeFilePath(stripQueryAndHash(stripFileProtocol(input))))
 
-    // Remove initial root prefix, if it's a complete match or followed by /
</file context>
Fix with Cubic

}

function navigateToProject(directory: string | undefined) {
async function navigateToProject(directory: string | undefined) {
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

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

P2: Making navigateToProject async without updating callers risks unhandled promise rejections. None of the 6+ call sites await or .catch() the returned promise. Wrap the body in a top-level try/catch so the function never rejects when called fire-and-forget.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app/src/pages/layout.tsx, line 1097:

<comment>Making `navigateToProject` async without updating callers risks unhandled promise rejections. None of the 6+ call sites `await` or `.catch()` the returned promise. Wrap the body in a top-level `try/catch` so the function never rejects when called fire-and-forget.</comment>

<file context>
@@ -1093,14 +1094,51 @@ export default function Layout(props: ParentProps) {
   }
 
-  function navigateToProject(directory: string | undefined) {
+  async function navigateToProject(directory: string | undefined) {
     if (!directory) return
     const root = projectRoot(directory)
</file context>
Fix with Cubic

const issues = (errorInput.data.issues ?? []).map((issue) => {
return `${issue.path.join(".")}: ${issue.message}`
})
if (issues.length) return [head, file, "", ...issues].filter(Boolean).join("\n")
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

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

P2: The empty string "" in the array is always removed by filter(Boolean), so it has no effect. If a blank line separator between the file and issues was intended, this is a bug — you'd need to filter first, then insert the separator. If no separator was intended, remove the "" to avoid confusion.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app/src/utils/server-errors.ts, line 30:

<comment>The empty string `""` in the array is always removed by `filter(Boolean)`, so it has no effect. If a blank line separator between the file and issues was intended, this is a bug — you'd need to filter first, then insert the separator. If no separator was intended, remove the `""` to avoid confusion.</comment>

<file context>
@@ -0,0 +1,32 @@
+  const issues = (errorInput.data.issues ?? []).map((issue) => {
+    return `${issue.path.join(".")}: ${issue.message}`
+  })
+  if (issues.length) return [head, file, "", ...issues].filter(Boolean).join("\n")
+  return [head, file, detail].filter(Boolean).join("\n")
+}
</file context>
Fix with Cubic

return o.name === "ConfigInvalidError" && typeof o.data === "object" && o.data !== null
}

export function parseReabaleConfigInvalidError(errorInput: ConfigInvalidError) {
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

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

P2: Typo in exported function name: parseReabaleConfigInvalidErrorparseReadableConfigInvalidError. Since this is a new public API, fixing the name now avoids a breaking rename later.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app/src/utils/server-errors.ts, line 23:

<comment>Typo in exported function name: `parseReabaleConfigInvalidError` → `parseReadableConfigInvalidError`. Since this is a new public API, fixing the name now avoids a breaking rename later.</comment>

<file context>
@@ -0,0 +1,32 @@
+  return o.name === "ConfigInvalidError" && typeof o.data === "object" && o.data !== null
+}
+
+export function parseReabaleConfigInvalidError(errorInput: ConfigInvalidError) {
+  const head = "Invalid configuration"
+  const file = errorInput.data.path && errorInput.data.path !== "config" ? errorInput.data.path : ""
</file context>
Fix with Cubic

<div class="flex items-center gap-1.5">
<div>{language.t("session.tab.review")}</div>
<Show when={hasReview()}>
<div>{reviewCount()}</div>
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

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

P2: Review count badge lost all styling classes (typography, color, padding, rounded pill background). This appears to be an unintentional UI regression — the count will render as plain unstyled text instead of the previous pill-shaped badge.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app/src/pages/session/session-side-panel.tsx, line 240:

<comment>Review count badge lost all styling classes (typography, color, padding, rounded pill background). This appears to be an unintentional UI regression — the count will render as plain unstyled text instead of the previous pill-shaped badge.</comment>

<file context>
@@ -202,133 +216,128 @@ export function SessionSidePanel(props: {
+                        <div class="flex items-center gap-1.5">
+                          <div>{language.t("session.tab.review")}</div>
+                          <Show when={hasReview()}>
+                            <div>{reviewCount()}</div>
+                          </Show>
+                        </div>
</file context>
Fix with Cubic


return {
...data,
autoAccept:
Copy link

@cubic-dev-ai cubic-dev-ai bot Feb 27, 2026

Choose a reason for hiding this comment

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

P3: Migration leaves stale autoAcceptEdits key in persisted data. The spread ...data copies all old keys, and the merge() function in persist.ts preserves extra keys not in defaults. Consider destructuring out the old key to keep persisted data clean.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/app/src/context/permission.tsx, line 67:

<comment>Migration leaves stale `autoAcceptEdits` key in persisted data. The spread `...data` copies all old keys, and the `merge()` function in `persist.ts` preserves extra keys not in defaults. Consider destructuring out the old key to keep persisted data clean.</comment>

<file context>
@@ -61,9 +54,25 @@ export const { use: usePermission, provider: PermissionProvider } = createSimple
+
+          return {
+            ...data,
+            autoAccept:
+              typeof data.autoAcceptEdits === "object" && data.autoAcceptEdits && !Array.isArray(data.autoAcceptEdits)
+                ? data.autoAcceptEdits
</file context>
Fix with Cubic

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces significant performance enhancements for the TUI by optimizing state updates during LLM streaming. It also refactors TUI configuration into a dedicated file for better organization and improves file rendering and commenting features across the application. Underlying infrastructure for process execution and Windows compatibility has been made more robust, and a new subscription tier, 'OpenCode Go', has been integrated into the console.

Highlights

  • Performance Optimization (TUI): Replaced reconcile() calls with direct assignment and path-syntax setStore() for streaming updates in the TUI, significantly reducing deep-diffing overhead and improving performance during LLM streaming.
  • TUI Configuration Refactoring: Migrated TUI-specific settings (theme, keybinds, scroll behavior) from opencode.json to a new dedicated tui.json file, including a migration script to automatically move existing settings.
  • Unified File Component: Refactored the UI to use a single File component for displaying code, diffs, images, and other file content, replacing separate Code and Diff components for simplified rendering.
  • Enhanced Commenting and Review Features: Improved line commenting functionality in the TUI, adding support for updating and deleting comments, better focus management, and displaying comments directly within message parts.
  • Robust Process Execution: Introduced a new Process API to replace direct Bun.spawn and $ shell calls, enhancing the reliability, testability, and cross-platform compatibility of external command execution.
  • OpenCode Go Subscription: Implemented a new 'OpenCode Go' subscription plan in the console, including new UI sections, API routes, and billing logic for usage tracking and management.
  • Windows Compatibility Improvements: Enhanced Windows path normalization, app path resolution, and shell environment loading in the desktop app, with specific handling for PowerShell integration.
  • Storybook Integration: Added a new packages/storybook project to facilitate UI component development and testing with Storybook.
Changelog
  • .github/actions/setup-bun/action.yml
    • Updated Bun setup action to allow specifying a download URL for baseline versions.
  • .opencode/agent/glossary/README.md
    • Renamed to .opencode/glossary/README.md.
  • .opencode/agent/glossary/ar.md
    • Renamed to .opencode/glossary/ar.md.
  • .opencode/agent/glossary/br.md
    • Renamed to .opencode/glossary/br.md.
  • .opencode/agent/glossary/bs.md
    • Renamed to .opencode/glossary/bs.md.
  • .opencode/agent/glossary/da.md
    • Renamed to .opencode/glossary/da.md.
  • .opencode/agent/glossary/de.md
    • Renamed to .opencode/glossary/de.md.
  • .opencode/agent/glossary/es.md
    • Renamed to .opencode/glossary/es.md.
  • .opencode/agent/glossary/fr.md
    • Renamed to .opencode/glossary/fr.md.
  • .opencode/agent/glossary/ja.md
    • Renamed to .opencode/glossary/ja.md.
  • .opencode/agent/glossary/ko.md
    • Renamed to .opencode/glossary/ko.md.
  • .opencode/agent/glossary/no.md
    • Renamed to .opencode/glossary/no.md.
  • .opencode/agent/glossary/pl.md
    • Renamed to .opencode/glossary/pl.md.
  • .opencode/agent/glossary/ru.md
    • Renamed to .opencode/glossary/ru.md.
  • .opencode/agent/glossary/th.md
    • Renamed to .opencode/glossary/th.md.
  • .opencode/agent/glossary/zh-cn.md
    • Renamed to .opencode/glossary/zh-cn.md.
  • .opencode/agent/glossary/zh-tw.md
    • Renamed to .opencode/glossary/zh-tw.md.
  • .opencode/agent/translator.md
    • Updated path for locale-specific glossary guidance.
  • bun.lock
    • Updated various @opencode-ai/* package versions from 1.2.10 to 1.2.15.
    • Added new Storybook-related dependencies.
    • Updated several testing libraries.
  • infra/console.ts
    • Changed product name from 'OpenCode Lite' to 'OpenCode Go'.
  • nix/hashes.json
    • Updated Bun lockfile hashes.
  • package.json
    • Updated packageManager Bun version from 1.3.9 to 1.3.10.
  • packages/app/e2e/files/file-tree.spec.ts
    • Updated file viewer locator to [data-component="file"][data-mode="text"].
  • packages/app/e2e/files/file-viewer.spec.ts
    • Added modKey import.
    • Added a new test case for cmd+f to open search in the file viewer.
    • Updated file viewer locators from code to viewer.
  • packages/app/e2e/projects/projects-switch.spec.ts
    • Added sessionPath import.
    • Removed unused stamp variable.
    • Added error handling for base64Decode.
    • Refactored session creation and navigation logic to use createSdk and sessionPath.
  • packages/app/e2e/session/session-composer-dock.spec.ts
    • Removed seedSessionPermission import.
    • Added PermissionRule type.
    • Refactored withDockSession to accept permission options.
    • Added clearPermissionDock and withMockPermission functions.
    • Updated permission-related tests to use withMockPermission.
    • Added new tests for child session question and permission requests.
  • packages/app/e2e/utils.ts
    • Changed serverHost default from localhost to 127.0.0.1.
  • packages/app/package.json
    • Updated package version from 1.2.10 to 1.2.15.
  • packages/app/playwright.config.ts
    • Changed baseURL and serverHost defaults from localhost to 127.0.0.1.
  • packages/app/src/app.tsx
    • Replaced Code and Diff components with File component.
    • Replaced CodeComponentProvider and DiffComponentProvider with FileComponentProvider.
  • packages/app/src/components/dialog-select-model-unpaid.tsx
    • Added 'OpenCode Go' tagline and recommended tag.
  • packages/app/src/components/dialog-select-provider.tsx
    • Added 'OpenCode Go' tagline and recommended tag.
  • packages/app/src/components/file-tree.tsx
    • Removed Tooltip import and FileTreeNodeTooltip component.
    • Removed tooltip prop from FileTree and FileTreeNode.
  • packages/app/src/components/prompt-input.tsx
    • Imported selectionFromLines and SelectedLineRange.
    • Updated history types to include PromptHistoryEntry, PromptHistoryStoredEntry, and PromptHistoryComment.
    • Added historyComments and applyHistoryComments functions.
    • Modified applyHistoryPrompt to handle comments.
    • Updated addToHistory and handleHistoryNavigation to pass/use comments.
    • Added accepting memo and updated permission auto-accept UI logic.
  • packages/app/src/components/prompt-input/build-request-parts.test.ts
    • Added a test case to verify opencodeComment metadata in synthetic text parts.
  • packages/app/src/components/prompt-input/build-request-parts.ts
    • Imported createCommentMetadata and formatCommentNote.
    • Replaced commentNote function with formatCommentNote and added opencodeComment metadata to synthetic text parts.
  • packages/app/src/components/prompt-input/history.test.ts
    • Added tests for prependHistoryEntry with comments.
    • Added tests for navigatePromptHistory preserving comments.
    • Added tests for normalizePromptHistoryEntry.
  • packages/app/src/components/prompt-input/history.ts
    • Added types for PromptHistoryComment, PromptHistoryEntry, and PromptHistoryStoredEntry.
    • Added cloneSelection, clonePromptHistoryComments, and normalizePromptHistoryEntry functions.
    • Updated prependHistoryEntry and isPromptEqual to handle comments.
    • Modified navigatePromptHistory to return PromptHistoryEntry and manage comments.
  • packages/app/src/components/session/session-context-tab.tsx
    • Replaced Code component with File component.
  • packages/app/src/components/session/session-header.tsx
    • Reordered imports.
    • Refactored app opening logic to handle powershell specifically on Windows.
    • Added loading state for opening apps.
  • packages/app/src/components/session/session-sortable-tab.tsx
    • Updated FileVisual to use a Show component for active state and tab-fileicon-color/tab-fileicon-mono classes for better icon rendering.
    • Added gutter prop to TooltipKeybind.
  • packages/app/src/components/settings-providers.tsx
    • Added 'OpenCode Go' tagline and recommended tag.
  • packages/app/src/context/comments.test.ts
    • Added tests for update and replace methods in comment session.
  • packages/app/src/context/comments.tsx
    • Added cloneSelection, cloneComment, and group helper functions.
    • Added update and replace methods to the comment session state.
  • packages/app/src/context/file/path.test.ts
    • Updated expected normalized Windows paths to use \ instead of /.
  • packages/app/src/context/file/path.ts
    • Modified normalize function to handle Windows paths more robustly, preserving native separators and improving root prefix stripping.
  • packages/app/src/context/file/view-cache.ts
    • Cloned SelectedLineRange in normalizeSelectedLines to prevent unintended mutations.
  • packages/app/src/context/global-sdk.tsx
    • Added staleDeltas set and deltaKey function to prevent processing stale message.part.delta events if a message.part.updated event for the same part has already been processed.
  • packages/app/src/context/global-sync.tsx
    • Removed errorMessage function.
    • Used formatServerError for toast messages.
  • packages/app/src/context/global-sync/bootstrap.ts
    • Imported formatServerError.
    • Used formatServerError for toast messages.
  • packages/app/src/context/layout-scroll.test.ts
    • Added a test for reseeding empty cache after persisted snapshot loads.
  • packages/app/src/context/layout-scroll.ts
    • Modified seed function to reseed empty cache if a new snapshot is available.
  • packages/app/src/context/permission.tsx
    • Removed shouldAutoAccept function.
    • Modified hasPermissionPromptRules to check all config values.
    • Updated auto-accept logic to use store.autoAccept instead of store.autoAcceptEdits and removed shouldAutoAccept check.
    • Added migration logic for autoAcceptEdits to autoAccept.
  • packages/app/src/context/prompt.tsx
    • Added isCommentItem helper.
    • Added removeComment, updateComment, and replaceComments methods to prompt context.
  • packages/app/src/hooks/use-providers.ts
    • Added opencode-go to popularProviders.
  • packages/app/src/i18n/ar.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/br.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/bs.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/da.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/de.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/en.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/es.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/fr.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/ja.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/ko.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/no.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/pl.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/ru.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/th.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/tr.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/zh.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/i18n/zht.ts
    • Updated translations for 'Auto-accept edits' to 'Auto-accept permissions'.
    • Added taglines for 'OpenCode Go'.
  • packages/app/src/pages/directory-layout.tsx
    • Removed useSDK import.
    • Removed onPermissionRespond, onQuestionReply, and onQuestionReject props from DataProvider.
  • packages/app/src/pages/layout.tsx
    • Imported latestRootSession.
    • Refactored navigateToProject to find the latest session across workspaces and sandboxes, and to use createSdk for session creation.
  • packages/app/src/pages/layout/helpers.test.ts
    • Added tests for latestRootSession.
  • packages/app/src/pages/layout/helpers.ts
    • Added latestRootSession function to find the most recently updated root session across multiple workspaces.
  • packages/app/src/pages/layout/sidebar-project.tsx
    • Refactored ProjectTile and SortableProject to use a store for open, menu, and suppressHover states.
    • Added logic to toggle sidebar on project click if already selected.
  • packages/app/src/pages/session.tsx
    • Updated centered memo logic.
    • Modified message navigation to use store.messageId and handle targetIndex up to msgs.length.
    • Added updateCommentInContext, removeCommentFromContext, reviewCommentActions, isEditableTarget, and deepActiveElement functions.
    • Updated handleKeyDown to prevent autofocus on editable targets.
    • Modified reviewTab memo.
    • Changed ChangesDropdown size and valueClass.
    • Updated SessionReviewTab props to include comment update/delete actions and actions object.
    • Removed a createEffect block related to tab activation.
  • packages/app/src/pages/session/composer/session-composer-state.test.ts
    • Added tests for sessionPermissionRequest and sessionQuestionRequest to verify correct request tree traversal.
  • packages/app/src/pages/session/composer/session-composer-state.ts
    • Refactored createSessionComposerBlocked and questionRequest/permissionRequest memos to use sessionPermissionRequest and sessionQuestionRequest for hierarchical request resolution.
  • packages/app/src/pages/session/composer/session-request-tree.ts
    • Added sessionTreeRequest generic helper and sessionPermissionRequest/sessionQuestionRequest functions to traverse session hierarchy for pending requests.
  • packages/app/src/pages/session/file-tabs.tsx
    • Replaced useCodeComponent with useFileComponent.
    • Removed image/SVG/binary file rendering logic, delegating it to the File component.
    • Integrated createLineCommentController for line comment management.
    • Added FileCommentMenu component.
    • Added cmd+f shortcut for search.
  • packages/app/src/pages/session/helpers.test.ts
    • Updated expected call order in createOpenReviewFile test.
  • packages/app/src/pages/session/helpers.ts
    • Modified createOpenReviewFile to handle loadFile returning a Promise, ensuring tab opening happens after file load.
  • packages/app/src/pages/session/message-timeline.tsx
    • Added messageComments helper function to extract comments from message parts.
    • Added UI to display these comments above session turns.
  • packages/app/src/pages/session/review-tab.tsx
    • Removed createStore for stuck state in StickyAddButton.
    • Refactored scroll restoration logic to use queueRestore and userInteracted flag.
    • Added event listeners for user interaction to scrollRef.
    • Added onLineCommentUpdate, onLineCommentDelete, and lineCommentActions props to SessionReviewTab.
  • packages/app/src/pages/session/session-side-panel.tsx
    • Updated reviewTab memo.
    • Added fileTreeScrolled state and syncFileTreeScrolled function.
    • Added changesEl and allEl refs.
    • Updated Tabs.List for file tree to use data-scrolled attribute.
    • Added onScroll handlers to Tabs.Content for file tree.
    • Changed drag preview to use data-component="tabs-drag-preview".
  • packages/app/src/pages/session/use-session-hash-scroll.ts
    • Added logic to account for sticky session title height when scrolling to a message.
  • packages/app/src/utils/comment-note.ts
    • Added types and functions for creating, reading, formatting, and parsing comment metadata in prompt parts.
  • packages/app/src/utils/server-errors.test.ts
    • Added tests for formatServerError and parseReabaleConfigInvalidError.
  • packages/app/src/utils/server-errors.ts
    • Defined ConfigInvalidError type and formatServerError/parseReabaleConfigInvalidError functions for consistent error formatting.
  • packages/console/app/package.json
    • Updated package version from 1.2.10 to 1.2.15.
    • Modified build script to generate tui.json schema.
  • packages/console/app/src/i18n/ar.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/br.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/da.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/de.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/en.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/es.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/fr.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/it.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/ja.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/ko.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/no.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/pl.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/ru.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/th.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/tr.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/zh.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/i18n/zht.ts
    • Added translations for 'Black plan enrollment paused' and 'OpenCode Go' subscription details.
    • Updated usage breakdown labels.
  • packages/console/app/src/routes/black.css
    • Added CSS for [data-slot="paused"].
  • packages/console/app/src/routes/black/index.tsx
    • Imported Resource and query/createAsync for getPaused.
    • Added paused memo and Show component to display a 'paused' message if enrollment is paused.
  • packages/console/app/src/routes/black/subscribe/[plan].tsx
    • Imported Resource and query/createAsync for getEnabled.
    • Added enabled memo and Show component to conditionally render the subscription form.
  • packages/console/app/src/routes/stripe/webhook.ts
    • Refactored webhook handling to differentiate between 'lite' and 'black' subscriptions.
    • Updated customer.subscription.created to handle 'lite' subscriptions.
    • Updated customer.subscription.updated and customer.subscription.deleted to call unsubscribeLite or unsubscribeBlack based on product ID.
    • Updated invoice.payment_succeeded to set enrichment.type to 'lite' if applicable.
  • packages/console/app/src/routes/workspace/[id]/billing/black-section.tsx
    • Changed Billing.subscribe to Billing.subscribeBlack.
  • packages/console/app/src/routes/workspace/[id]/billing/index.tsx
    • Imported LiteSection.
    • Added isBlack memo.
    • Added Show components to conditionally render BlackSection or LiteSection.
  • packages/console/app/src/routes/workspace/[id]/billing/lite-section.module.css
    • Added CSS for the Lite section.
  • packages/console/app/src/routes/workspace/[id]/billing/lite-section.tsx
    • Added LiteSection component for managing OpenCode Go subscriptions, including usage tracking, use balance toggle, and promotional content.
  • packages/console/app/src/routes/workspace/[id]/graph-section.tsx
    • Updated getCosts to include plan in usage data.
    • Modified dailyData maps and datasets creation to handle 'lite' plans.
    • Updated legend item color logic.
  • packages/console/app/src/routes/workspace/[id]/usage-section.tsx
    • Updated UsageSection to use Switch/Match for usage.enrichment?.plan to display 'Black', 'Go', or 'BYOK' subscription amounts.
  • packages/console/app/src/routes/workspace/common.tsx
    • Added lite and liteSubscriptionID to queryBillingInfo result.
  • packages/console/app/src/routes/zen/go/v1/messages.ts
    • Added a new endpoint for Anthropic-style messages for 'lite' models.
  • packages/console/app/src/routes/zen/lite/v1/chat/completions.ts
    • Renamed to packages/console/app/src/routes/zen/go/v1/chat/completions.ts.
  • packages/console/app/src/routes/zen/util/handler.ts
    • Imported LiteData and Resource.
    • Renamed FREE_WORKSPACES to ADMIN_WORKSPACES.
    • Added ADMIN_WORKSPACES check for alpha- models in production.
    • Updated metric logging for subscription type.
    • Updated authInfo to include black and lite subscriptions.
    • Added validation for 'lite' subscription billing, including weekly, monthly, and rolling limits.
    • Updated enrichment logic for UsageTable to include 'lite' and 'byok' plans.
  • packages/console/app/src/routes/zen/v1/models.ts
    • Added filter to exclude alpha- models from the public model list.
  • packages/console/core/migrations/20260224043338_nifty_starjammers/migration.sql
    • Added lite table and lite_subscription_id/lite columns to billing table.
  • packages/console/core/migrations/20260224043338_nifty_starjammers/snapshot.json
    • Added Drizzle snapshot for the new lite table and billing table changes.
  • packages/console/core/package.json
    • Updated package version from 1.2.10 to 1.2.15.
  • packages/console/core/script/black-select-workspaces.ts
    • Changed SubscriptionPlan to BlackPlans.
  • packages/console/core/script/lookup-user.ts
    • Changed SubscriptionPlan to BlackPlans.
  • packages/console/core/src/billing.ts
    • Imported LiteTable.
    • Imported LiteData.
    • Added generateLiteCheckoutUrl function.
    • Changed subscribe to subscribeBlack.
    • Changed unsubscribe to unsubscribeBlack.
    • Added unsubscribeLite function.
  • packages/console/core/src/black.ts
    • Changed SubscriptionPlan to BlackPlans.
    • Added productID function.
  • packages/console/core/src/identifier.ts
    • Added lite identifier.
  • packages/console/core/src/lite.ts
    • Removed fixedLimit.
    • Added weeklyLimit and monthlyLimit.
    • Removed planToPriceID and priceIDToPlan.
    • Added productID, priceID, and planName.
  • packages/console/core/src/schema/billing.sql.ts
    • Changed SubscriptionPlan to BlackPlans.
    • Added liteSubscriptionID and lite columns to BillingTable.
    • Added LiteTable.
  • packages/console/core/src/subscription.ts
    • Imported getMonthlyBounds.
    • Changed usagePercent calculation from ceil to floor.
    • Added analyzeMonthlyUsage function.
  • packages/console/core/src/util/date.test.ts
    • Removed file.
  • packages/console/core/src/util/date.ts
    • Added getMonthlyBounds function.
  • packages/console/core/test/date.test.ts
    • Added tests for getWeekBounds and getMonthlyBounds.
  • packages/console/core/test/subscription.test.ts
    • Added tests for Subscription.analyzeMonthlyUsage.
  • packages/console/function/package.json
    • Updated package version from 1.2.10 to 1.2.15.
  • packages/console/function/src/log-processor.ts
    • Added new zen/go paths to the pathname check.
  • packages/console/mail/package.json
    • Updated package version from 1.2.10 to 1.2.15.
  • packages/desktop/README.md
    • Updated prerequisites and troubleshooting sections.
    • Removed bun run --cwd packages/desktop dev command.
  • packages/desktop/package.json
    • Updated package version from 1.2.10 to 1.2.15.
  • packages/desktop/src-tauri/Cargo.lock
    • Updated windows-core and windows-sys versions.
  • packages/desktop/src-tauri/Cargo.toml
    • Updated windows dependency to windows-sys and windows-core.
  • packages/desktop/src-tauri/src/cli.rs
    • Imported HashMap, Path, Stdio, Instant.
    • Changed CREATE_NO_WINDOW/CREATE_SUSPENDED to use windows_sys.
    • Added SHELL_ENV_TIMEOUT.
    • Added parse_shell_env, command_output_with_timeout, probe_shell_env, is_nushell, load_shell_env, and merge_shell_env functions for shell environment probing.
    • Modified spawn_command to use load_shell_env and merge_shell_env.
    • Changed cmd.args(["-il", "-c", &line]) to cmd.args(["-l", "-c", &line]).
    • Added #[cfg(test)] block with tests for shell environment parsing and merging.
  • packages/desktop/src-tauri/src/lib.rs
    • Imported os module.
    • Updated check_app_exists and resolve_app_path to use os::windows functions.
    • Added open_in_powershell command.
  • packages/desktop/src-tauri/src/os/mod.rs
    • Added windows module.
  • packages/desktop/src-tauri/src/os/windows.rs
    • Added check_windows_app, resolve_windows_app_path, and open_in_powershell functions for Windows-specific app and shell handling.
  • packages/desktop/src/bindings.ts
    • Added openInPowershell command.
  • packages/desktop/src/index.tsx
    • Removed resolvedApp variable in openPath for Windows.
    • Added logic to handle powershell specifically by calling commands.openInPowershell.
  • packages/enterprise/package.json
    • Updated package version from 1.2.10 to 1.2.15.
  • packages/enterprise/src/routes/share/[shareID].tsx
    • Replaced DiffComponentProvider/CodeComponentProvider with FileComponentProvider.
    • Replaced SSRDiff with FileSSR.
  • packages/extensions/zed/extension.toml
    • Updated version from 1.2.10 to 1.2.15 and corresponding archive URLs.
  • packages/function/package.json
    • Updated package version from 1.2.10 to 1.2.15.
  • packages/function/src/log-processor.ts
    • Added new zen/go paths to the pathname check.
  • packages/opencode/BUN_SHELL_MIGRATION_PLAN.md
    • Added a migration plan for replacing Bun shell template-tag usage with a unified Process API.
  • packages/opencode/package.json
    • Updated package version from 1.2.10 to 1.2.15.
  • packages/opencode/script/schema.ts
    • Imported TuiConfig.
    • Refactored generate function to be reusable.
    • Modified script to generate schema for TuiConfig.Info if tuiFile is provided.
  • packages/opencode/src/acp/agent.ts
    • Imported ToolPart.
    • Added bashSnapshots and toolStarts to ACPClient state.
    • Refactored tool handling to use toolStart and bashOutput functions.
    • Added logic to track bash output snapshots and prevent duplicate tool_call_update events for identical output.
    • Cleared toolStarts and bashSnapshots on tool completion/error.
    • Added metadata to rawOutput for error tools.
  • packages/opencode/src/bun/index.ts
    • Imported text from node:stream/consumers and Process.
    • Changed Bun.spawn to Process.spawn.
    • Updated readableStreamToText to text.
    • Updated Error message.
    • Added || process.env.CI to --no-cache condition.
  • packages/opencode/src/bun/registry.ts
    • Imported text from node:stream/consumers and Process.
    • Changed Bun.spawn to Process.spawn.
    • Updated readableStreamToText to text.
  • packages/opencode/src/cli/cmd/auth.ts
    • Imported Process and text.
    • Changed Bun.spawn to Process.spawn.
    • Updated new Response(proc.stdout).text() to text(proc.stdout).
  • packages/opencode/src/cli/cmd/session.ts
    • Imported Process.
    • Changed Bun.spawn to Process.spawn.
    • Added check for proc.stdin before writing.
  • packages/opencode/src/cli/cmd/tui/app.tsx
    • Imported TuiConfigProvider and TuiConfig.
    • Added config prop to tui function and passed it to TuiConfigProvider.
  • packages/opencode/src/cli/cmd/tui/component/dialog-command.tsx
    • Changed KeybindsConfig import to KeybindKey.
  • packages/opencode/src/cli/cmd/tui/component/dialog-provider.tsx
    • Updated PROVIDER_PRIORITY to include opencode-go and reordered.
    • Added opencode-go tagline.
  • packages/opencode/src/cli/cmd/tui/component/tips.tsx
    • Updated tips related to config files and keybinds to reflect the new tui.json separation.
  • packages/opencode/src/cli/cmd/tui/context/keybind.tsx
    • Changed useSync to useTuiConfig.
    • Changed KeybindsConfig to TuiConfig.Info["keybinds"].
    • Updated keybinds memo to use config.keybinds.
  • packages/opencode/src/cli/cmd/tui/context/sync.tsx
    • Removed reconcile import.
    • Updated setStore calls to directly assign values instead of using reconcile or produce for performance optimization.
  • packages/opencode/src/cli/cmd/tui/context/theme.tsx
    • Removed useSync import.
    • Changed sync.data.config.theme to config.theme.
  • packages/opencode/src/cli/cmd/tui/context/tui-config.tsx
    • Added TuiConfigProvider context.
  • packages/opencode/src/cli/cmd/tui/routes/session/index.tsx
    • Imported useTuiConfig.
    • Added tui to context.
    • Updated scrollAcceleration to use tuiConfig.
    • Removed diagnostics memo from Write and Edit components.
    • Added Diagnostics component.
    • Updated view memo in Edit and ApplyPatch to use ctx.tui.diff_style.
    • Added Diagnostics component to ApplyPatch.
  • packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx
    • Imported useTuiConfig.
    • Updated diffStyle to use config.diff_style.
  • packages/opencode/src/cli/cmd/tui/thread.ts
    • Imported TuiConfig and Instance.
    • Added config to tui function call.
  • packages/opencode/src/cli/cmd/tui/util/clipboard.ts
    • Imported Process.
    • Changed Bun.spawn to Process.spawn.
    • Added checks for proc.stdin.
  • packages/opencode/src/cli/cmd/tui/util/editor.ts
    • Imported Process.
    • Changed Bun.spawn to Process.spawn.
  • packages/opencode/src/cli/cmd/workspace-serve.ts
    • Added WorkspaceServeCommand for starting a remote workspace websocket server.
  • packages/opencode/src/config/config.ts
    • Imported fileURLToPath, createRequire, and ConfigPaths.
    • Renamed getManagedConfigDir to systemManagedConfigDir.
    • Added managedConfigDir function.
    • Renamed merge to mergeConfigConcatArrays.
    • Updated config loading logic to use ConfigPaths for project files and directories.
    • Removed keybinds default parsing.
    • Removed TUI schema.
    • Updated loadFile and load to use ConfigPaths.readFile and ConfigPaths.parseText.
    • Removed legacy TUI key handling from load.
    • Updated plugin resolution to use createRequire as a fallback.
    • Removed JsonError and InvalidError definitions, now imported from ConfigPaths.
  • packages/opencode/src/config/markdown.ts
    • Changed frontmatter.split("\n") to frontmatter.split(/\r?\n/) for cross-platform line ending compatibility.
  • packages/opencode/src/config/migrate-tui-config.ts
    • Added migrateTuiConfig function to move TUI-specific settings from opencode.json to tui.json. Includes backup and stripping logic.
  • packages/opencode/src/config/paths.ts
    • Added ConfigPaths namespace with helpers for resolving config files, parsing JSONC, and handling substitutions.
  • packages/opencode/src/config/tui-schema.ts
    • Defined TuiOptions and TuiInfo Zod schemas for TUI-specific configuration.
  • packages/opencode/src/config/tui.ts
    • Added TuiConfig namespace for loading and managing TUI-specific configuration, including migration from legacy opencode.json settings.
  • packages/opencode/src/file/ignore.ts
    • Changed filepath.split(sep) to filepath.split(/[\/]/) for cross-platform path splitting.
  • packages/opencode/src/file/ripgrep.ts
    • Imported Process and text.
    • Changed Bun.spawn to Process.spawn.
    • Updated readableStreamToText to text.
    • Changed Bun.readableStreamToText to text.
    • Refactored rg function to use Process.spawn and iterate over proc.stdout as an async iterable.
  • packages/opencode/src/file/time.ts
    • Added a 50ms tolerance to mtime check for Windows NTFS timestamp fuzziness.
  • packages/opencode/src/flag/flag.ts
    • Added OPENCODE_TUI_CONFIG declaration and dynamic getter.
  • packages/opencode/src/format/formatter.ts
    • Imported text and Process.
    • Changed Bun.spawn to Process.spawn.
    • Updated readableStreamToText to text.
  • packages/opencode/src/format/index.ts
    • Imported Process.
    • Changed Bun.spawn to Process.spawn.
  • packages/opencode/src/index.ts
    • Imported WorkspaceServeCommand.
    • Conditionally added WorkspaceServeCommand to cli if Installation.isLocal().
  • packages/opencode/src/lsp/server.ts
    • Imported text and Process.
    • Changed Bun.spawn to Process.spawn.
    • Updated readableStreamToText to text.
  • packages/opencode/src/project/project.ts
    • Changed void Filesystem.write to await Filesystem.write.
  • packages/opencode/src/pty/index.ts
    • Removed Subscriber, sockets, owners, socketCounter, tagSocket, and token functions.
    • Refactored subscribers map to use unknown as key.
    • Updated connect and cleanup logic to use ws.data as connection key.
  • packages/opencode/src/server/routes/session.ts
    • Added session.deleteMessage route.
  • packages/opencode/src/session/index.ts
    • Added removeMessage function to delete a message and its parts, including a sessionID check in the where clause.
    • Updated removePart to include a sessionID check.
  • packages/opencode/src/snapshot/index.ts
    • Added core.longpaths, core.symlinks, and core.fsmonitor git configs to init.
    • Added core.longpaths and core.symlinks to diff, restore, revert, and diffFull git commands.
  • packages/opencode/src/storage/db.ts
    • Added state object to hold sqlite instance.
    • Added close function to close the database and reset client.
  • packages/opencode/src/tool/grep.ts
    • Imported text and Process.
    • Changed Bun.spawn to Process.spawn.
    • Updated new Response(proc.stdout).text() to text(proc.stdout) and new Response(proc.stderr).text() to text(proc.stderr).
  • packages/opencode/src/tool/plan.ts
    • Removed ENTER_DESCRIPTION import and PlanEnterTool export.
    • Commented out PlanEnterTool definition.
  • packages/opencode/src/tool/registry.ts
    • Imported PlanExitTool.
    • Removed PlanEnterTool import.
    • Updated tools array to only include PlanExitTool when OPENCODE_EXPERIMENTAL_PLAN_MODE is enabled.
    • Updated plugin import to use pathToFileURL(match).href.
  • packages/opencode/src/util/git.ts
    • Replaced Bun's $ shell usage with Process.run for git commands, ensuring stdin: "ignore" to prevent deadlocks in ACP client environments.
  • packages/opencode/src/util/process.ts
    • Added Process namespace with Options, RunOptions, Result, RunFailedError types and spawn, run functions for robust child process management.
  • packages/opencode/test/acp/event-subscription.test.ts
    • Added inProgressText, isToolCallUpdate, and toolEvent helper functions.
    • Added sessionUpdates to createFakeAgent return.
    • Added tests for streaming bash output snapshots, de-duping identical snapshots, emitting synthetic pending states, and clearing bash snapshot markers.
  • packages/opencode/test/config/config.test.ts
    • Added a test to ignore legacy TUI keys in opencode.json.
    • Updated tests for environment variable and file inclusion substitution to use username instead of theme.
    • Updated managedConfigDir test to remove theme assertion.
  • packages/opencode/test/config/markdown.test.ts
    • Updated expectation for content trimming to handle \r\n cross-platform.
  • packages/opencode/test/config/tui.test.ts
    • Added comprehensive tests for TUI config loading, precedence, migration from opencode.json, JSONC parsing, and error handling.
  • packages/opencode/test/ide/ide.test.ts
    • Changed structuredClone(process.env) to { ...process.env } for original env variable.
  • packages/opencode/test/preload.ts
    • Imported Database.
    • Added Database.close() in afterAll.
    • Added robust fs.rm retry logic with Bun.gc to handle Windows EBUSY errors during test cleanup.
  • packages/opencode/test/pty/pty-output-isolation.test.ts
    • Updated test description and logic to reflect that in-place socket data mutation is treated as the same connection, rather than preventing leaks.
  • packages/opencode/test/skill/discovery.test.ts
    • Updated agentsSdk path check to use path.sep for cross-platform compatibility.
  • packages/opencode/test/snapshot/snapshot.test.ts
    • Imported path.
    • Added fwd helper function for cross-platform path normalization in expectations.
    • Updated all snapshot tests to use fwd for path assertions and added core.longpaths, core.symlinks, core.fsmonitor git configs.
    • Changed ln -s to fs.symlink for symlink creation.
  • packages/opencode/test/tool/bash.test.ts
    • Imported os.
    • Updated workdir to use os.tmpdir() and expected patterns to use path.join for cross-platform compatibility.
    • Added eol variable for result.output expectation.
  • packages/opencode/test/tool/external-directory.test.ts
    • Updated expected patterns to use replaceAll("\\", "/") for cross-platform compatibility.
  • packages/opencode/test/tool/write.test.ts
    • Replaced test for 'throws error for paths outside project' with 'throws error when OS denies write access' using a read-only file.
  • packages/opencode/test/util/glob.test.ts
    • Updated expect(results).toEqual to use path.join for cross-platform compatibility.
  • packages/ui/src/components/diff.css
    • Renamed to packages/ui/src/components/file.css.
  • packages/ui/src/components/line-comment.css
    • Renamed to packages/ui/src/components/line-comment-styles.ts.
Ignored Files
  • Ignored by pattern: .github/workflows/** (8)
    • .github/workflows/beta.yml
    • .github/workflows/compliance-close.yml
    • .github/workflows/docs-locale-sync.yml
    • .github/workflows/pr-standards.yml
    • .github/workflows/test.yml
    • .github/workflows/vouch-check-issue.yml
    • .github/workflows/vouch-check-pr.yml
    • .github/workflows/vouch-manage-by-issue.yml
Activity
  • The pull request was created by coleleavitt.
  • The pull request title indicates a performance optimization for the TUI related to state updates.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Comment on lines +33 to +50
async function conflicts() {
const out = await $`git diff --name-only --diff-filter=U`.text().catch(() => "")
return out
.split("\n")
.map((x) => x.trim())
.filter(Boolean)
}

async function cleanup() {
try {
await $`git merge --abort`
} catch {}
try {
await $`git checkout -- .`
} catch {}
try {
await $`git clean -fd`
} catch {}

Choose a reason for hiding this comment

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

Action required

1. cleanup() swallows command errors 📘 Rule violation ⛯ Reliability

conflicts() and cleanup() intentionally ignore failures via empty catch {} / `.catch(() =>
"")`, which can hide critical merge/cleanup failures and make incident debugging difficult. This
violates the requirement to handle failure points with meaningful context and avoid unnecessary
try/catch usage.
Agent Prompt
## Issue description
`script/beta.ts` currently swallows errors from git commands (empty `catch {}`) and suppresses errors when reading conflicts (`.catch(() => "")`). This can hide real failures and makes diagnosing merge/cleanup issues difficult.

## Issue Context
The compliance checklist requires robust error handling with actionable context, and discourages broad/unnecessary try/catch blocks.

## Fix Focus Areas
- script/beta.ts[33-50]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines 307 to 313
setStore(
"part",
event.properties.messageID,
produce((draft) => {
const part = draft[result.index]
const field = event.properties.field as keyof typeof part
const existing = part[field] as string | undefined
;(part[field] as string) = (existing ?? "") + event.properties.delta
}),
result.index,
event.properties.field as any,
(prev: string) => (prev ?? "") + event.properties.delta,
)

Choose a reason for hiding this comment

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

Action required

2. field cast to any 📘 Rule violation ✓ Correctness

The streaming update path uses event.properties.field as any when calling setStore, erasing type
safety for store writes and risking runtime updates to invalid/non-string fields. This introduces
new any usage in a hot-path update.
Agent Prompt
## Issue description
`event.properties.field` is cast to `any` when calling `setStore(...)`, which defeats type checking for store updates.

## Issue Context
This is on the streaming hot path (`message.part.delta`). The field appears to be intended as a key into a message-part object, and the update function assumes the field holds a string.

## Fix Focus Areas
- packages/opencode/src/cli/cmd/tui/context/sync.tsx[307-313]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +8 to +10
parseApiKey: (headers: Headers) => headers.get("x-api-key") ?? undefined,
parseModel: (url: string, body: any) => body.model,
parseIsStream: (url: string, body: any) => !!body.stream,

Choose a reason for hiding this comment

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

Action required

3. Post body typed any 📘 Rule violation ✓ Correctness

The new POST handler parses request body as any, which removes compile-time guarantees for
external input handling and can mask missing validation/narrowing. This introduces any into
request parsing code.
Agent Prompt
## Issue description
The request `body` is typed as `any` in `parseModel`/`parseIsStream`, which erases type safety for external inputs.

## Issue Context
This is a new API route handler; input bodies should be typed safely (e.g., `unknown` + narrowing) rather than `any`.

## Fix Focus Areas
- packages/console/app/src/routes/zen/go/v1/messages.ts[8-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

)
}

function Diagnostics(props: { diagnostics?: Record<string, Record<string, any>[]>; filePath: string }) {

Choose a reason for hiding this comment

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

Action required

4. Diagnostics typed with any 📘 Rule violation ✓ Correctness

Diagnostics introduces any in its diagnostics prop type (`Record<string, Record<string,
any>[]>`), undermining type safety for LSP diagnostic rendering and making misuse easy. This
violates the no-any requirement in new code.
Agent Prompt
## Issue description
`Diagnostics` uses `any` in its prop typing, which removes type safety for diagnostic rendering.

## Issue Context
The component reads `severity`, `range.start.line`, `range.start.character`, and `message`, so the type can be specified structurally or by importing an existing diagnostic type.

## Fix Focus Areas
- packages/opencode/src/cli/cmd/tui/routes/session/index.tsx[2141-2147]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines 24 to +35
showAllFiles: () => void
tabForPath: (path: string) => string
openTab: (tab: string) => void
loadFile: (path: string) => void
loadFile: (path: string) => any | Promise<void>
}) => {
return (path: string) => {
batch(() => {
input.showAllFiles()
input.openTab(input.tabForPath(path))
input.loadFile(path)
const maybePromise = input.loadFile(path)
const openTab = () => input.openTab(input.tabForPath(path))
if (maybePromise instanceof Promise) maybePromise.then(openTab)
else openTab()

Choose a reason for hiding this comment

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

Action required

5. loadfile returns any 📘 Rule violation ✓ Correctness

createOpenReviewFile changes loadFile to return any | Promise<void>, introducing any where
void | Promise<void> (or a typed result) would suffice. This reduces type safety and violates the
no-any rule.
Agent Prompt
## Issue description
`loadFile` is typed as returning `any | Promise<void>`, introducing `any` unnecessarily.

## Issue Context
The implementation only needs to distinguish between synchronous `void` and async `Promise<void>`.

## Fix Focus Areas
- packages/app/src/pages/session/helpers.ts[23-35]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +78 to +118
<button
type="button"
aria-label={props.buttonLabel}
data-slot="line-comment-button"
on:mousedown={(e) => e.stopPropagation()}
on:mouseup={(e) => e.stopPropagation()}
on:click={props.onClick as any}
on:mouseenter={props.onMouseEnter as any}
>
<Show
when={props.inline}
fallback={<Icon name={icon() === "plus" ? "plus-small" : "comment"} size="small" />}
>
<InlineGlyph icon={icon()} />
</Show>
</button>
<Show when={props.open}>
<div
data-slot="line-comment-popover"
classList={{
[props.popoverClass ?? ""]: !!props.popoverClass,
}}
on:mousedown={(e) => e.stopPropagation()}
on:focusout={props.onPopoverFocusOut as any}
>
{props.children}
</div>
</Show>
</>
}
>
<div
data-slot="line-comment-popover"
data-inline-body=""
classList={{
[props.popoverClass ?? ""]: !!props.popoverClass,
}}
onFocusOut={props.onPopoverFocusOut}
on:mousedown={(e) => e.stopPropagation()}
on:click={props.onClick as any}
on:mouseenter={props.onMouseEnter as any}
on:focusout={props.onPopoverFocusOut as any}

Choose a reason for hiding this comment

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

Action required

7. Linecomment handlers cast any 📘 Rule violation ✓ Correctness

LineCommentAnchor casts event handlers to any for on:click, on:mouseenter, and
on:focusout, hiding potential mismatches between handler signatures and actual events. This
introduces any into UI event wiring.
Agent Prompt
## Issue description
`line-comment.tsx` uses `as any` casts for event handlers, removing type safety for UI event wiring.

## Issue Context
The component already defines handler props; they should be passed without `any` by ensuring the prop types match the events used (or by adapting via small wrapper functions).

## Fix Focus Areas
- packages/ui/src/components/line-comment.tsx[78-118]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +334 to +336
const setHighlights = (ranges: FileFindHit[], currentIndex: number) => {
const api = (globalThis as unknown as { CSS?: { highlights?: any }; Highlight?: any }).CSS?.highlights
const Highlight = (globalThis as unknown as { Highlight?: any }).Highlight

Choose a reason for hiding this comment

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

Action required

8. Highlights types use any 📘 Rule violation ✓ Correctness

setHighlights() uses any in global typing for CSS.highlights and Highlight, which disables
type checking for a browser API integration. This introduces avoidable any into new logic.
Agent Prompt
## Issue description
The highlight API integration uses `any` in global type assertions, defeating type checking.

## Issue Context
Only a small surface is needed (`set`, `delete`, and `new Highlight(...)`); define minimal interfaces/types and keep runtime feature detection.

## Fix Focus Areas
- packages/ui/src/pierre/file-find.ts[334-336]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +496 to +504
test("streams running bash output snapshots and de-dupes identical snapshots", async () => {
await using tmp = await tmpdir()
await Instance.provide({
directory: tmp.path,
fn: async () => {
const { agent, controller, sessionUpdates, stop } = createFakeAgent()
const cwd = "/tmp/opencode-acp-test"
const sessionId = await agent.newSession({ cwd, mcpServers: [] } as any).then((x) => x.sessionId)
const input = { command: "echo hello", description: "run command" }

Choose a reason for hiding this comment

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

Action required

9. Test casts newsession args 📘 Rule violation ✓ Correctness

The new ACP event-subscription tests cast agent.newSession(...) arguments to any, weakening type
safety and potentially hiding incorrect test setup. This introduces new any usage in tests.
Agent Prompt
## Issue description
The test calls `agent.newSession(...)` with an argument cast to `any`, which bypasses type checking.

## Issue Context
Tests should still preserve type safety; prefer `satisfies` or importing the correct input type for `newSession`.

## Fix Focus Areas
- packages/opencode/test/acp/event-subscription.test.ts[496-504]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +150 to 168
await Database.transaction(async (tx) => {
await tx
.update(BillingTable)
.set({
customerID,
liteSubscriptionID: subscriptionID,
lite: {},
paymentMethodID: paymentMethod.id,
paymentMethodLast4: paymentMethod.card?.last4 ?? null,
paymentMethodType: paymentMethod.type,
})
.where(eq(BillingTable.workspaceID, workspaceID))

await tx.insert(PaymentTable).values({
workspaceID,
id: Identifier.create("payment"),
amount: centsToMicroCents(amountInCents),
paymentID,
invoiceID,
customerID,
enrichment: {
type: "subscription",
couponID,
},
await tx.insert(LiteTable).values({
workspaceID,
id: Identifier.create("lite"),
userID: userID,
})
})

Choose a reason for hiding this comment

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

Action required

10. Lite webhook unique constraint loop 🐞 Bug ⛯ Reliability

customer.subscription.created (lite) unconditionally inserts into LiteTable, which has a unique
index on (workspaceID,userID). Any Stripe retry/duplicate delivery will hit a unique constraint
error and return 500, causing repeated retries and leaving the workspace in a stuck/partial billing
state.
Agent Prompt
### Issue description
`customer.subscription.created` for `type === "lite"` inserts into `LiteTable` unconditionally, but `LiteTable` enforces a unique index on `(workspaceID, userID)`. Stripe webhooks are at-least-once; duplicates/retries will cause a constraint error and repeated 500 responses.

### Issue Context
The handler should be idempotent: if the Lite row already exists for the workspace/user, the webhook should treat it as success.

### Fix Focus Areas
- packages/console/app/src/routes/stripe/webhook.ts[150-168]
- packages/console/core/src/schema/billing.sql.ts[62-76]

### Implementation notes
- Prefer a DB-level upsert (e.g., `onDuplicateKeyUpdate` / equivalent) for `LiteTable` keyed by `(workspaceID, userID)`.
- Alternatively, `select` then `insert` only if absent, but ensure it’s race-safe.
- Consider also persisting a Stripe event id (or subscription id) to dedupe across retries.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +121 to 134
// get payment id from invoice
const invoice = await Billing.stripe().invoices.retrieve(invoiceID, {
expand: ["payments"],
})
const paymentID = invoice.payments?.data[0].payment.payment_intent as string
if (!paymentID) throw new Error("Payment ID not found")

// set customer metadata
if (!billing?.customerID) {
await Billing.stripe().customers.update(customerID, {
metadata: {
workspaceID,
},
})
}
// get payment method for the payment intent
const paymentIntent = await Billing.stripe().paymentIntents.retrieve(paymentID, {
expand: ["payment_method"],
})
const paymentMethod = paymentIntent.payment_method
if (!paymentMethod || typeof paymentMethod === "string") throw new Error("Payment method not expanded")

Choose a reason for hiding this comment

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

Action required

11. Lite webhook requires payment intent 🐞 Bug ✓ Correctness

The Lite customer.subscription.created handler throws if the latest invoice has no payment
intent/payment method. The same webhook file explicitly acknowledges payment intents can be
undefined when coupons are used, so Lite subscriptions with discounts/trials can fail with 500 and
never provision Lite access.
Agent Prompt
### Issue description
The Lite `customer.subscription.created` handler assumes `latest_invoice` always has a payment intent and expanded payment method. For coupon/trial scenarios, Stripe may not create a payment intent, and the code will throw, returning 500.

### Issue Context
The same webhook already documents that `paymentID` can be undefined with coupons in another path; Lite should follow similar robustness.

### Fix Focus Areas
- packages/console/app/src/routes/stripe/webhook.ts[121-134]
- packages/console/app/src/routes/stripe/webhook.ts[218-226]

### Implementation notes
- If `paymentID` is missing, proceed with Lite provisioning and skip updating `paymentMethod*` fields (or fetch default payment method from `customer.invoice_settings.default_payment_method` / subscription defaults).
- Ensure the handler still returns 2xx when state is consistent, to avoid Stripe retry storms.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This is an impressive pull request that delivers significant performance improvements and a wide range of refactorings and features. The core change of replacing reconcile() with path-syntax setStore is a crucial optimization for the TUI's responsiveness during LLM streaming. I appreciate the detailed analysis in the PR description that motivated this change.

The broader refactoring efforts are also excellent. Separating TUI-specific configuration into tui.json with an automatic migration script is a great move for maintainability and user experience. The introduction of a new Process utility to centralize command execution is a major architectural improvement, enhancing robustness and consistency across the codebase. Furthermore, the UI enhancements, such as the new comment management features, improved keyboard navigation, and fixes for scroll positioning, all contribute to a more polished and reliable user experience.

I have one suggestion regarding the shell environment loading on desktop, but overall, this is a very high-quality contribution.


let mut cmd = Command::new(shell);
cmd.args(["-il", "-c", &line]);
cmd.args(["-l", "-c", &line]);

Choose a reason for hiding this comment

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

high

It seems the command to spawn the user's shell now uses -l (login shell) instead of the previously used -il (interactive login shell). While the new load_shell_env function correctly attempts to use -il first, this spawn_command function, which is on a critical path, does not.

Using only -l might not load the user's full environment (like aliases, functions, and PATH modifications from files like .bashrc or .zshrc), which could lead to commands not being found. An interactive shell (-i) is generally required to source these files. I recommend changing this back to -il to ensure the spawned process inherits the complete user environment, which is crucial for reliability.

Suggested change
cmd.args(["-l", "-c", &line]);
cmd.args(["-il", "-c", &line]);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.