fix: isolate PawWork runtime storage#126
Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 3 minutes and 23 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (18)
📝 WalkthroughWalkthroughThis PR migrates the application namespace from "opencode" to "pawwork" across storage keys, configuration identifiers, database filenames, and environment variables. It introduces a Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes The PR spans 50+ files across multiple packages with heterogeneous changes: repetitive storage key migrations (low cognitive load per item but high volume), a new Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsTimed out fetching pipeline failures after 30000ms Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a namespacing system to support both OpenCode and PawWork runtimes by updating storage keys, configuration filenames, and directory paths based on the PAWWORK_RUNTIME_NAMESPACE environment variable. Feedback focuses on ensuring backward compatibility for legacy configuration files, updating storage eviction logic to prevent quota issues with the new prefix, and refining the Electron initialization process to correctly show the loading UI during database migrations. Additionally, suggestions were made to improve configuration discovery for JSONC variants and to use preferred filenames when migrating from legacy formats.
4190608 to
b7392ab
Compare
Review SummaryPosted 8 inline comments with nitpick-level feedback. Key themes: Code Quality
Consistency Issues
Test Quality
API Design
These are all P2/nitpick items - no blocking issues. The core runtime namespace isolation logic appears sound. Addressing the helper duplication and symmetry concerns would improve maintainability. |
b7392ab to
7ffd855
Compare
There was a problem hiding this comment.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/desktop-electron/src/main/index.ts (1)
246-252: 🧹 Nitpick | 🔵 TrivialDead code path:
needsMigrationis alwaysfalse.Since
needsMigrationis hardcoded tofalseon line 206, this conditional block can never execute. Consider removing it to reduce maintenance burden.🧹 Proposed cleanup
- if (needsMigration) { - const show = await Promise.race([loadingTask.then(() => false), delay(1_000).then(() => true)]) - if (show) { - overlay = createLoadingWindow(globals) - await delay(1_000) - } - }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/desktop-electron/src/main/index.ts` around lines 246 - 252, The if-block guarded by the always-false needsMigration (the block using loadingTask, delay, createLoadingWindow and overlay) is dead code; remove that conditional and its inner code (including creation/awaits of loadingTask/delay/show and the overlay assignment) and then delete or refactor any now-unused symbols (needsMigration, overlay, or related imports/usages) so there are no dangling references to createLoadingWindow, loadingTask or delay in this module.packages/app/e2e/commands/panels.spec.ts (1)
179-207:⚠️ Potential issue | 🟡 MinorRename test constant to SCREAMING_SNAKE_CASE.
const keyshould follow the test constant naming rule to avoid style drift.💡 Suggested rename
- const key = "pawwork.global.dat:layout" - const raw = localStorage.getItem(key) + const LAYOUT_STORAGE_KEY = "pawwork.global.dat:layout" + const raw = localStorage.getItem(LAYOUT_STORAGE_KEY) @@ - key, + LAYOUT_STORAGE_KEY,As per coding guidelines "Use SCREAMING_SNAKE_CASE for constants in tests".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/app/e2e/commands/panels.spec.ts` around lines 179 - 207, Rename the test constant "key" to a SCREAMING_SNAKE_CASE name (e.g., STORAGE_KEY or LAYOUT_KEY) and update all its usages in this snippet (the declaration currently "const key" and the calls to localStorage.getItem(key) and localStorage.setItem(key, ...)) to use the new constant; ensure you only change the identifier and not the surrounding logic in the parsing/assignment blocks (variables referenced: key, raw, parsed, review, sessionView, current).
♻️ Duplicate comments (1)
packages/desktop-electron/src/main/server.ts (1)
79-82:⚠️ Potential issue | 🟠 Major
prepareServerEnvstill mutates globalprocess.envin a test-reachable path.This is the same isolation risk raised earlier: global env mutation can leak across concurrently executing tests/callers. Prefer a non-mutating contract where possible, or make mutation semantics explicit and tightly scoped to bootstrap-only usage.
Also applies to: 84-84
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/desktop-electron/src/main/server.ts` around lines 79 - 82, prepareServerEnv currently mutates the global process.env (via Object.assign(process.env, buildServerEnv(password))), which can leak environment across tests; change its contract to avoid implicit global mutation by having prepareServerEnv return the env object (e.g., return buildServerEnv(password)) or accept an explicit targetEnv parameter to apply to, and update callers of prepareServerEnv (the bootstrap/bootstrap-only usage) to either call Object.assign(process.env, returnedEnv) in a tightly scoped bootstrap path or pass an explicit env object when needed; keep the mutation explicit and documented so only the bootstrap entrypoint mutates process.env and tests can use the returned env without affecting globals.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/app/e2e/settings/settings.spec.ts`:
- Line 88: Update the test title string in the test(...) declaration that
currently reads "unknown theme ids migrate to pawwork and clear cached CSS" to
use lowercase for "css" — e.g., "unknown theme ids migrate to pawwork and clear
cached css"; locate the test call (the test function around the identifier used
in settings.spec.ts) and replace only the title text, ensuring any references to
the exact test name (if used elsewhere) are updated to match.
In `@packages/desktop-electron/src/main/index-sidecar-source.test.ts`:
- Around line 3-11: The test currently uses fragile string matching against the
raw source; change it to parse the source into an AST (e.g., via `@babel/parser`
or acorn) and assert on AST nodes: parse the contents read into the variable
source, find the exported/object node that contains a Property with key
"username" whose value is a MemberExpression referencing
PAWWORK_RUNTIME.serverUsername, find a VariableDeclarator/Identifier named
needsMigration with an init BooleanLiteral false, assert there is no Identifier
or CallExpression name "sqliteFileExists", and assert there is no StringLiteral
with value "opencode"; update the test body to walk/query the AST for these
patterns instead of using expect(source).toContain/toNotContain so formatting
changes won’t break the assertions.
In `@packages/desktop-electron/src/main/server.test.ts`:
- Line 62: Remove the brittle substring assertion
`expect(Object.values(roots).every((value) =>
!value.toLowerCase().includes("opencode"))).toBeTrue()` and instead assert the
exact shape/values of the `roots` object; update the test around the `roots`
variable to use strict equality checks (e.g., compare `roots` or
`Object.values(roots)` to the expected array/object) so the test relies only on
explicit expected paths already asserted elsewhere in this test.
In `@packages/opencode/src/config/config.ts`:
- Around line 52-53: PROJECT_CONFIG_NAMES incorrectly includes "pawwork" causing
OpenCode to load PawWork configs; remove "pawwork" from the PROJECT_CONFIG_NAMES
constant so PROJECT_CONFIG_FILES no longer contains pawwork.json/pawwork.jsonc,
and update any related directory-scan logic that treats ".pawwork" as a config
directory (references: PROJECT_CONFIG_NAMES, PROJECT_CONFIG_FILES and the config
scanning/merging code paths noted around the other occurrences) to prevent
merging .pawwork/* into OpenCode project config.
- Around line 52-59: PROJECT_CONFIG_FILES currently includes pawwork.json* and
OPENCODE_GLOBAL_CONFIG_FILES is set to PROJECT_CONFIG_FILES, which lets OpenCode
treat pawwork.json* as its global config; change OPENCODE_GLOBAL_CONFIG_FILES so
it excludes pawwork.json and pawwork.jsonc (for example, build it from
PROJECT_CONFIG_NAMES without "pawwork" or filter PROJECT_CONFIG_FILES to remove
files that start with "pawwork") so that globalConfigFiles(), loadGlobal(),
globalConfigFile(), and updateGlobal() will never consider pawwork.json* as an
OpenCode global config.
In `@packages/opencode/test/config/pawwork-global-config.test.ts`:
- Around line 17-47: Replace the bespoke Effect runner helpers (load, save,
saveGlobal, clear, ready, listConfigDirs) and manual layer composition (layer,
infra, emptyAccount, emptyAuth, AppFileSystem.defaultLayer usage) with the repo
test helpers: refactor tests to call testEffect(...) from test/lib/effect.ts and
use provideInstance(...) or provideTmpdirInstance(...) to supply the Config
layer and filesystem mocks instead of Effect.runPromise +
.pipe(Effect.provide(...)). Locate usages of the functions
load/save/saveGlobal/clear/ready/listConfigDirs and the Layer construction
(Layer.provide/Layer.provideMerge etc.) in this file and convert each test to
return a testEffect that provides the required instance via
provideInstance/provideTmpdirInstance, invoking Config.Service.use(...) inside
the Effect passed to testEffect rather than using the custom runPromise
wrappers.
- Around line 101-123: The test "PawWork runtime mode computes Global config
under PawWork before module load" currently hardcodes /tmp paths in the spawned
subprocess script and assertions; replace those with a fixture-managed temporary
directory by calling tmpdir() from fixture/fixture.ts and creating the
runtime/project paths under that temp dir, use the `await using` pattern to
ensure automatic cleanup, and update the script string and the subprocess
env/arguments so ConfigPaths.directories("/tmp/project", "/tmp/project") becomes
ConfigPaths.directories("<tmpdir>/project", "<tmpdir>/project") (use the tmpdir
value injected into the script), and similarly change the expected assertion to
reference the fixture temp path instead of
"/tmp/pawwork-config-root-test/pawwork"; apply the same replacement for the
other occurrences noted (lines ~330-382).
In `@packages/opencode/test/index-runtime-namespace.test.ts`:
- Around line 7-8: Replace the brittle exact-string assertions in the test that
check for "Database.getChannelPath()" and the literal
'path.join(Global.Path.data, "opencode.db")' with regex-based assertions so
formatting/quote changes won’t break the test; update the two expect(...) calls
in index-runtime-namespace.test.ts to use
expect(source).toMatch(/Database\.getChannelPath\(\)/) (or a looser pattern
matching the call) and
expect(source).not.toMatch(/path\.join\(\s*Global\.Path\.data\s*,\s*["'`]opencode\.db["'`]\s*\)/)
to verify structure rather than exact string formatting.
In `@packages/opencode/test/provider/provider.test.ts`:
- Around line 58-79: Replace the raw Jest async test with the Effect-based test
harness: import and use testEffect(...) (from test/lib/effect.ts) and wrap the
body in Effect.gen(...) so the test uses the Effect runtime; move the
tmpdir/Instance.provide logic inside Effect.gen and use Effect-friendly helpers
for env manipulation (replace direct process.env mutation with the provided
set/restore Effects or runEffect-wrapped set calls) while keeping the same
assertions against list() and ProviderID.make("opencode"/"opencode-go") —
specifically, change the top-level test(...) to testEffect(..., () =>
Effect.gen(function*() { ... })) and put the tmpdir, Instance.provide,
set("OPENCODE_API_KEY", ...), and the expects inside that generator so the test
executes under the Effect test harness.
In `@packages/opencode/test/storage/db.test.ts`:
- Around line 20-23: The test's expected channel list is missing "prod" which
makes the assertion diverge from Database.getChannelPath() behavior; update the
test so the condition includes "prod" alongside "latest" and "beta" when
computing expected (the code referencing Installation.CHANNEL and
Database.getChannelPath() should now treat "latest", "beta", and "prod" as the
short-path cases) so the expected path calculation matches runtime logic that
maps those channels to pawwork.db and others to pawwork-<sanitized-channel>.db.
In `@packages/shared/test/global.test.ts`:
- Around line 7-10: The tests hardcode POSIX /tmp paths which fail on Windows;
change the setup in packages/shared/test/global.test.ts to build the temp root
using the platform temp directory (e.g., os.tmpdir()) and construct XDG_* paths
with path.join(os.tmpdir(), 'pawwork-shared-runtime-test',
'share'|'cache'|'config'|'state') instead of "/tmp/...", and update the
corresponding assertions (including the similar assertions referenced at lines
32-41) to compare against those constructed paths so the tests are
platform-independent; locate and update the environment assignments and
assertions in the global.test.ts file (the XDG_* env setup and its later
expectations).
- Around line 11-13: The generated test helper currently only sets
process.env.PAWWORK_RUNTIME_NAMESPACE when namespace is provided, leaving any
existing value intact when namespace is undefined; update the template in
packages/shared/test/global.test.ts so that when ${JSON.stringify(namespace)} is
undefined you explicitly clear the environment variable (e.g., delete
process.env.PAWWORK_RUNTIME_NAMESPACE or set it to undefined) instead of doing
nothing, ensuring tests like "defaults to OpenCode namespace outside PawWork
desktop" are order-independent; modify the conditional around
PAWWORK_RUNTIME_NAMESPACE in the snippet so it has an else branch that clears
the variable.
In `@packages/ui/src/theme/context.tsx`:
- Around line 11-16: STORAGE_KEYS was renamed which will reset existing users'
theme prefs; add a one-time migration run during initialization (e.g., in the
same onMount/init function that reads/writes theme state) that maps old keys
(e.g., "opencode-theme-id", "opencode-color-scheme", "opencode-theme-css-light",
"opencode-theme-css-dark") to the new STORAGE_KEYS entries, and for each key:
read the old value, if present and the new key is empty then write the value to
STORAGE_KEYS (use existing read/write helpers) and then drop the old key (use
drop) so users keep their preferences; implement this as a migrateOldKeys helper
and call it before any code that reads from STORAGE_KEYS or initializes theme
state.
---
Outside diff comments:
In `@packages/app/e2e/commands/panels.spec.ts`:
- Around line 179-207: Rename the test constant "key" to a SCREAMING_SNAKE_CASE
name (e.g., STORAGE_KEY or LAYOUT_KEY) and update all its usages in this snippet
(the declaration currently "const key" and the calls to
localStorage.getItem(key) and localStorage.setItem(key, ...)) to use the new
constant; ensure you only change the identifier and not the surrounding logic in
the parsing/assignment blocks (variables referenced: key, raw, parsed, review,
sessionView, current).
In `@packages/desktop-electron/src/main/index.ts`:
- Around line 246-252: The if-block guarded by the always-false needsMigration
(the block using loadingTask, delay, createLoadingWindow and overlay) is dead
code; remove that conditional and its inner code (including creation/awaits of
loadingTask/delay/show and the overlay assignment) and then delete or refactor
any now-unused symbols (needsMigration, overlay, or related imports/usages) so
there are no dangling references to createLoadingWindow, loadingTask or delay in
this module.
---
Duplicate comments:
In `@packages/desktop-electron/src/main/server.ts`:
- Around line 79-82: prepareServerEnv currently mutates the global process.env
(via Object.assign(process.env, buildServerEnv(password))), which can leak
environment across tests; change its contract to avoid implicit global mutation
by having prepareServerEnv return the env object (e.g., return
buildServerEnv(password)) or accept an explicit targetEnv parameter to apply to,
and update callers of prepareServerEnv (the bootstrap/bootstrap-only usage) to
either call Object.assign(process.env, returnedEnv) in a tightly scoped
bootstrap path or pass an explicit env object when needed; keep the mutation
explicit and documented so only the bootstrap entrypoint mutates process.env and
tests can use the returned env without affecting globals.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: bce1e9df-6150-4963-b653-3ed5acc6be07
📒 Files selected for processing (50)
packages/app/e2e/app/server-default.spec.tspackages/app/e2e/commands/panels.spec.tspackages/app/e2e/fixtures.tspackages/app/e2e/settings/settings.spec.tspackages/app/e2e/sidebar/sidebar-session-search.spec.tspackages/app/e2e/sidebar/sidebar.spec.tspackages/app/e2e/utils.tspackages/app/public/oc-theme-preload.jspackages/app/src/context/language.tsxpackages/app/src/context/terminal.tsxpackages/app/src/entry.tsxpackages/app/src/hooks/use-providers.test.tspackages/app/src/pages/session/terminal-panel.tsxpackages/app/src/theme-preload.test.tspackages/app/src/utils/persist.test.tspackages/app/src/utils/persist.tspackages/desktop-electron/scripts/ci-smoke.test.tspackages/desktop-electron/scripts/ci-smoke.tspackages/desktop-electron/src/main/constants.tspackages/desktop-electron/src/main/index-sidecar-source.test.tspackages/desktop-electron/src/main/index.tspackages/desktop-electron/src/main/runtime-namespace.tspackages/desktop-electron/src/main/server.test.tspackages/desktop-electron/src/main/server.tspackages/desktop-electron/src/main/store.tspackages/desktop-electron/src/renderer/i18n/index.tspackages/desktop-electron/src/renderer/index.tsxpackages/opencode/src/agent/agent.tspackages/opencode/src/config/config.tspackages/opencode/src/config/managed.tspackages/opencode/src/config/paths.tspackages/opencode/src/global/index.tspackages/opencode/src/index.tspackages/opencode/src/session/instruction.tspackages/opencode/src/session/session.tspackages/opencode/src/storage/db.tspackages/opencode/test/agent/agent.test.tspackages/opencode/test/config/config.test.tspackages/opencode/test/config/pawwork-global-config.test.tspackages/opencode/test/global/runtime-namespace.test.tspackages/opencode/test/index-runtime-namespace.test.tspackages/opencode/test/provider/provider.test.tspackages/opencode/test/session/instruction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/storage/db.test.tspackages/shared/src/global.tspackages/shared/src/runtime.tspackages/shared/test/global.test.tspackages/ui/src/theme/context.tsxpackages/ui/src/theme/loader.ts
59e9e7d to
3ba63a9
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
3ba63a9 to
f0a140e
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Astro-Han
left a comment
There was a problem hiding this comment.
Submitting pending review to clear it
Astro-Han
left a comment
There was a problem hiding this comment.
See inline comments for issues found.
f0a140e to
7092442
Compare
Summary
.opencodeconfig readable for compatibility in PawWork runtime, but skipped dependency installation there so PawWork does not generate package files under.opencode.Why
Closes #111. PawWork embeds the OpenCode engine, but users should not need OpenCode installed and PawWork should not create OpenCode-named local files or persisted records on their machine.
Related Issue
Closes #111
How To Verify
Also completed repeated fresh-eyes code review loops. The last review found no actionable P0, P1, P2, or P3 issues within scope.
Screenshots or Recordings
Not applicable. This PR changes local runtime namespace, storage keys, and tests, with no intended visible UI change.
Checklist
dev, and my PR title and commit messages use Conventional Commits in EnglishSummary by CodeRabbit
Release Notes
New Features
Improvements
Configuration