refactor: migrate selected runtime schemas to Effect Schema#280
refactor: migrate selected runtime schemas to Effect Schema#280
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughEffect schemas are now canonical with derived Zod statics (Info.zod); ZodPreprocess removed; Zod derivation centralized via a shared adapter; permission decoding/order logic changed (wildcards prioritized, legacy aliases suppressed); OpenAPI, validators, and tests updated to use Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
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 docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Code Review
This pull request refactors the configuration and permission schemas to use Effect Schema as the canonical source of truth, deriving Zod schemas through a zod() walker and a withStatics utility. It removes the __originalKeys and ZodPreprocess workarounds for permission ordering in favor of an order-independent model and standardizes identifier schemas across the codebase. Feedback was provided to improve the isZodType utility by using instanceof z.ZodType to ensure reliable schema identification and prevent redundant AST processing.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/opencode/test/session/session.test.ts (1)
126-166:⚠️ Potential issue | 🟠 MajorPrevent Bus subscription leaks on failed assertions (use try/finally).
unsub()(andSessionNs.remove(info.id)) run only after all assertions. If an assertion fails, the subscription may remain active and interfere with later tests.Wrap the “wait + assertions” section in
try/finallysounsub()+ cleanup always run:Suggested diff
const info = await SessionNs.create({}) @@ const unsub = Bus.subscribe(MessageV2.Event.PartUpdated, (event) => { received = event.properties.part as MessageV2.Part }) - - const tokens = { - total: 1500, - input: 500, - output: 800, - reasoning: 200, - cache: { read: 100, write: 50 }, - } - const partInput = { - id: PartID.ascending(), - messageID, - sessionID: info.id, - type: "step-finish" as const, - reason: "stop", - cost: 0.005, - tokens, - } - await SessionNs.updatePart(partInput) - await new Promise((resolve) => setTimeout(resolve, 100)) - - expect(received).toBeDefined() - expect(received!.type).toBe("step-finish") - const finish = received as MessageV2.StepFinishPart - expect(finish.tokens.input).toBe(500) - expect(finish.tokens.output).toBe(800) - expect(finish.tokens.reasoning).toBe(200) - expect(finish.tokens.total).toBe(1500) - expect(finish.tokens.cache.read).toBe(100) - expect(finish.tokens.cache.write).toBe(50) - expect(finish.cost).toBe(0.005) - expect(received).not.toBe(partInput) - - unsub() - await SessionNs.remove(info.id) + try { + const tokens = { + total: 1500, + input: 500, + output: 800, + reasoning: 200, + cache: { read: 100, write: 50 }, + } + const partInput = { + id: PartID.ascending(), + messageID, + sessionID: info.id, + type: "step-finish" as const, + reason: "stop", + cost: 0.005, + tokens, + } + await SessionNs.updatePart(partInput) + await new Promise((resolve) => setTimeout(resolve, 100)) + + expect(received).toBeDefined() + expect(received!.type).toBe("step-finish") + const finish = received as MessageV2.StepFinishPart + expect(finish.tokens.input).toBe(500) + expect(finish.tokens.output).toBe(800) + expect(finish.tokens.reasoning).toBe(200) + expect(finish.tokens.total).toBe(1500) + expect(finish.tokens.cache.read).toBe(100) + expect(finish.tokens.cache.write).toBe(50) + expect(finish.cost).toBe(0.005) + expect(received).not.toBe(partInput) + } finally { + unsub() + await SessionNs.remove(info.id) + } }, })This makes the test suite more resilient to intermittent failures.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/opencode/test/session/session.test.ts` around lines 126 - 166, The test currently subscribes with Bus.subscribe (captured by unsub) and calls unsub() + SessionNs.remove(info.id) only after assertions, risking subscription leaks on failures; wrap the section that awaits the event and performs all expect(...) checks in a try/finally block so that in the finally you always call unsub() and await SessionNs.remove(info.id) (leave PartID.ascending(), SessionNs.updatePart and the await new Promise(...) untouched), ensuring cleanup runs regardless of assertion outcomes.
🤖 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/opencode/src/config/permission.ts`:
- Around line 21-31: The StructWithRest decoder reorders top-level permission
keys which breaks the precedence expected by Permission.evaluate
(last-match-wins) because Permission.fromConfig currently flattens
Object.entries(permission) in the decoded order; to fix, preserve original
insertion order or explicitly sort entries in Permission.fromConfig before
building rules so wildcards do not incorrectly override explicit rules — update
Permission.fromConfig to read and use the preserved __originalKeys (if present
from StructWithRest) or perform a stable sort that places specific keys before
wildcards (referencing StructWithRest, Permission.fromConfig, and
Permission.evaluate) so config precedence matches tests.
In `@packages/opencode/src/session/projectors.ts`:
- Line 65: Replace the blind type assertion on data.info before inserting into
DB: instead of using "as Session.Info" pass a validated/narrowed Session.Info
object to Session.toRow; validate the payload (e.g., with an explicit type
guard, runtime checks, or a schema like zod) and handle failures (log/skip or
throw) before calling db.insert(SessionTable).values(Session.toRow(...)).run(),
referencing the data.info variable, Session.toRow, SessionTable and the
db.insert call to locate the change.
In `@packages/opencode/src/util/effect-zod.ts`:
- Around line 43-50: Replace the undocumented private-property check in
isZodType and guard the cast in zodObject: change isZodType(value) to use the
public API (e.g., return value instanceof z.ZodType) and in
zodObject(Schema.Top) after computing derived call a public instanceof check
(e.g., derived instanceof z.ZodObject) before casting; if the check fails, throw
or return a clear error instead of force-casting. Update references to isZodType
and zodObject accordingly so the code relies only on zod's public types.
In `@packages/opencode/test/session/session.test.ts`:
- Around line 123-129: The test currently assigns event.properties.part to
received as MessageV2.Part which can capture any union member; update the
Bus.subscribe handler (subscribing to MessageV2.Event.PartUpdated) to first
check the runtime discriminator (e.g., event.properties.part.type ===
"step-finish") and only then assign to received casted to
MessageV2.StepFinishPart (preserving any readonly→mutable cast if needed); this
ensures received is the narrow StepFinishPart type and prevents capturing
unrelated PartUpdated events.
---
Outside diff comments:
In `@packages/opencode/test/session/session.test.ts`:
- Around line 126-166: The test currently subscribes with Bus.subscribe
(captured by unsub) and calls unsub() + SessionNs.remove(info.id) only after
assertions, risking subscription leaks on failures; wrap the section that awaits
the event and performs all expect(...) checks in a try/finally block so that in
the finally you always call unsub() and await SessionNs.remove(info.id) (leave
PartID.ascending(), SessionNs.updatePart and the await new Promise(...)
untouched), ensuring cleanup runs regardless of assertion outcomes.
🪄 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: 953bf1f5-7134-4337-bea3-1ccd0bf19bcc
⛔ Files ignored due to path filters (1)
packages/sdk/js/src/v2/gen/types.gen.tsis excluded by!**/gen/**
📒 Files selected for processing (23)
packages/opencode/script/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/project/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/session/projectors.tspackages/opencode/src/session/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/tool/edit.tspackages/opencode/src/tool/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/test/config/config.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: smoke-macos-arm64
- GitHub Check: unit-windows-opencode-config-project
- GitHub Check: unit-windows-desktop
- GitHub Check: unit-windows-opencode-session
- GitHub Check: unit-windows-app
- GitHub Check: unit-windows-opencode-server-tools
- GitHub Check: unit-opencode
- GitHub Check: unit-desktop
- GitHub Check: typecheck
- GitHub Check: analyze-js-ts
- GitHub Check: e2e-artifacts
🧰 Additional context used
📓 Path-based instructions (2)
packages/opencode/**/*.ts
📄 CodeRabbit inference engine (packages/opencode/AGENTS.md)
packages/opencode/**/*.ts: UseEffect.gen(function* () { ... })for Effect composition
UseEffect.fn("Domain.method")for named/traced effects andEffect.fnUntracedfor internal helpers; these accept pipeable operators as extra arguments to avoid unnecessary outer.pipe()wrappers
UseEffect.callbackfor callback-based APIs
PreferDateTime.nowAsDateovernew Date(yield* Clock.currentTimeMillis)when you need aDatein Effect code
UseSchema.Classfor multi-field data in Effect schemas
Use branded schemas (Schema.brand) for single-value types in Effect
UseSchema.TaggedErrorClassfor typed errors in Effect schemas
UseSchema.Defectinstead ofunknownfor defect-like causes in Effect code
InEffect.gen/Effect.fn, preferyield* new MyError(...)overyield* Effect.fail(new MyError(...))for direct early-failure branches
UsemakeRuntimefromsrc/effect/run-service.tsfor all services; it returns{ runPromise, runFork, runCallback }backed by a sharedmemoMapthat deduplicates layers
UseInstanceStatefromsrc/effect/instance-state.tsfor per-directory or per-project state that needs per-instance cleanup; do work directly in theInstanceState.makeclosure whereScopedCachehandles run-once semantics
UseEffect.addFinalizerorEffect.acquireReleaseinside theInstanceState.makeclosure for cleanup (subscriptions, process teardown, etc.)
UseEffect.forkScopedinside theInstanceState.makeclosure for background stream consumers — the fiber is interrupted when the instance is disposed
PreferFileSystem.FileSysteminstead of rawfs/promisesfor effectful file I/O in Effect services
PreferChildProcessSpawner.ChildProcessSpawnerwithChildProcess.make(...)instead of custom process wrappers in Effect services
PreferHttpClient.HttpClientinstead of rawfetchin Effect services
PreferPath.Path,Config,Clock, andDateTimeservices when those concerns are already inside Effect code
For backgroun...
Files:
packages/opencode/script/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/edit.tspackages/opencode/src/server/instance/config.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/project/schema.tspackages/opencode/test/session/compaction.test.tspackages/opencode/src/tool/schema.tspackages/opencode/test/session/session.test.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/session/projectors.tspackages/opencode/test/config/config.test.ts
packages/opencode/test/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (packages/opencode/test/AGENTS.md)
packages/opencode/test/**/*.test.{ts,tsx}: Use thetmpdirfunction fromfixture/fixture.tsto create temporary directories for tests with automatic cleanup. Useawait usingsyntax to ensure automatic cleanup when the variable goes out of scope.
When using thetmpdirfunction with git repository support, pass thegit: trueoption to initialize a git repo with a root commit.
Use theconfigoption intmpdirto write anopencode.jsonconfig file during test setup by passing a partial Config.Info object.
Use theinitoption intmpdirto define custom setup functions that can return extra data accessible viatmp.extra, and use thedisposeoption for custom cleanup logic.
UsetestEffect(...)fromtest/lib/effect.tsfor tests that exercise Effect services or Effect-based workflows.
Useit.effect(...)when the test should run withTestClockandTestConsole. Useit.live(...)when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.
Prefer Effect-aware helpers fromfixture/fixture.tsover building manual runtimes in tests: usetmpdirScoped()for scoped temp directories,provideInstance(dir)(effect)for low-level binding without directory creation,provideTmpdirInstance(...)for single temp instance binding, orprovideTmpdirServer(...)for tests that also need the test LLM server.
Defineconst it = testEffect(...)near the top of the test file and keep the test body insideEffect.gen(function* () { ... }). Yield services directly withyield* MyService.Serviceoryield* MyTool.
Avoid customManagedRuntime,attach(...), or ad hocrun(...)wrappers in Effect tests whentestEffect(...)already provides the runtime.
When a test needs instance-local state, preferprovideTmpdirInstance(...)orprovideInstance(...)over manualInstance.provide(...)inside Promise-style tests.
Files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/config/config.test.ts
🧠 Learnings (36)
📓 Common learnings
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/opencode/test/provider/provider.test.ts:64-85
Timestamp: 2026-04-22T09:32:54.556Z
Learning: In `packages/opencode/test/provider/provider.test.ts`, the file intentionally uses AppRuntime-based async helpers (`run`, `list`, `getProvider`, etc.) rather than `testEffect(...)` for all tests. Converting individual tests to `testEffect` while leaving the rest on the async pattern would create internal inconsistency. A full harness migration of this file is the right approach if the pattern needs to change, but that should be a separate PR.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch-auth.test.ts:0-0
Timestamp: 2026-04-27T11:19:24.963Z
Learning: In `packages/opencode/test/tool/websearch-auth.test.ts` (Astro-Han/pawwork), the tests intentionally use a small local `runWith` runner with raw `bun:test` and `Effect.runPromise` rather than the `testEffect` harness. Each test case injects a custom in-memory `Auth.Service` layer; switching to `testEffect` would be style-only churn without changing risk coverage. Do not flag these tests as needing harness migration.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/mcp-exa.test.ts:1-186
Timestamp: 2026-04-27T11:18:47.332Z
Learning: In `packages/opencode/test/tool/mcp-exa.test.ts` (Astro-Han/pawwork), the tests intentionally use raw `bun:test` async cases with `Effect.runPromise(...)` and per-case `HttpClient.make(...)` fakes rather than the `testEffect(...)` harness. The maintainer has explicitly decided not to migrate, because the HttpClient fake wiring is itself the behavior under test and switching to `testEffect` would be style churn without changing risk coverage. Do not flag these tests as needing harness migration.
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use branded schemas (`Schema.brand`) for single-value types in Effect
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 211
File: packages/opencode/src/provider/models.ts:113-179
Timestamp: 2026-04-24T06:50:05.142Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/provider/models.ts`), `PublishModel` and `PublishProvider` intentionally duplicate fields from `Model` and `Provider` with looser optionality. The split is a deliberate behavior boundary: `PublishModel`/`PublishProvider` (with `.passthrough()`) are used for lenient publish-time validation of incoming catalogs, while `Model`/`Provider` are used for strict runtime normalization. Do not flag this duplication as a maintenance burden or suggest extracting a shared base schema — the explicit separation is intentional and tied to the models-refresh reliability path introduced in PR `#211`.
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Class` for multi-field data in Effect schemas
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Defect` instead of `unknown` for defect-like causes in Effect code
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:58.310Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:12.228Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/prompt.ts` and `packages/opencode/src/session/processor.ts`, PR `#264`), the loop-gate race condition between `buildLoopContext()` and `recordSyntheticBlock`/`recordSyntheticStop` is intentionally handled via idempotence guards (re-check sigKey presence / `hasStopped` inside the record helpers) rather than a full per-parent `Effect.Mutex`. Threading a `Map<MessageID, Mutex>` through the processor was considered too large a surface change for this edge case; the residual TOCTOU window only produces extra synthetic parts with no behavioral drift on the "turn ends" contract. A code comment documents the trade-off and points to a full-mutex follow-up if the race is observed in practice. Do NOT re-flag the absence of a per-parent mutex as a blocking issue in future reviews.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:23.748Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/config/`), `Schema.Struct` and `Schema.Class` are both valid and intentionally used patterns across config modules (e.g. `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`, `command.ts`). Do not flag `Schema.Struct` usage in config schemas as needing conversion to `Schema.Class` — the choice is driven by upstream alignment and local context, not inconsistency.
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use branded schemas (`Schema.brand`) for single-value types in Effect
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.tspackages/opencode/src/control-plane/schema.ts
📚 Learning: 2026-04-21T16:57:23.748Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:23.748Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/config/`), `Schema.Struct` and `Schema.Class` are both valid and intentionally used patterns across config modules (e.g. `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`, `command.ts`). Do not flag `Schema.Struct` usage in config schemas as needing conversion to `Schema.Class` — the choice is driven by upstream alignment and local context, not inconsistency.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/src/project/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/control-plane/schema.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Class` for multi-field data in Effect schemas
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.tspackages/opencode/src/control-plane/schema.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/drizzle.config.ts : Configure Drizzle Kit migrations with schema path `./src/**/*.sql.ts` and output path `./migration`
Applied to files:
packages/opencode/script/schema.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/src/**/*.sql.ts : Drizzle schema should live in `src/**/*.sql.ts`
Applied to files:
packages/opencode/script/schema.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Defect` instead of `unknown` for defect-like causes in Effect code
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/config.tspackages/opencode/src/control-plane/schema.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `config` option in `tmpdir` to write an `opencode.json` config file during test setup by passing a partial Config.Info object.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/src/config/config.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `testEffect(...)` from `test/lib/effect.ts` for tests that exercise Effect services or Effect-based workflows.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-27T11:19:24.963Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch-auth.test.ts:0-0
Timestamp: 2026-04-27T11:19:24.963Z
Learning: In `packages/opencode/test/tool/websearch-auth.test.ts` (Astro-Han/pawwork), the tests intentionally use a small local `runWith` runner with raw `bun:test` and `Effect.runPromise` rather than the `testEffect` harness. Each test case injects a custom in-memory `Auth.Service` layer; switching to `testEffect` would be style-only churn without changing risk coverage. Do not flag these tests as needing harness migration.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Define `const it = testEffect(...)` near the top of the test file and keep the test body inside `Effect.gen(function* () { ... })`. Yield services directly with `yield* MyService.Service` or `yield* MyTool`.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/config/agent.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` wrappers in Effect tests when `testEffect(...)` already provides the runtime.
Applied to files:
packages/opencode/test/util/effect-zod.test.ts
📚 Learning: 2026-04-27T11:18:47.332Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/mcp-exa.test.ts:1-186
Timestamp: 2026-04-27T11:18:47.332Z
Learning: In `packages/opencode/test/tool/mcp-exa.test.ts` (Astro-Han/pawwork), the tests intentionally use raw `bun:test` async cases with `Effect.runPromise(...)` and per-case `HttpClient.make(...)` fakes rather than the `testEffect(...)` harness. The maintainer has explicitly decided not to migrate, because the HttpClient fake wiring is itself the behavior under test and switching to `testEffect` would be style churn without changing risk coverage. Do not flag these tests as needing harness migration.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Prefer Effect-aware helpers from `fixture/fixture.ts` over building manual runtimes in tests: use `tmpdirScoped()` for scoped temp directories, `provideInstance(dir)(effect)` for low-level binding without directory creation, `provideTmpdirInstance(...)` for single temp instance binding, or `provideTmpdirServer(...)` for tests that also need the test LLM server.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-22T09:32:54.556Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/opencode/test/provider/provider.test.ts:64-85
Timestamp: 2026-04-22T09:32:54.556Z
Learning: In `packages/opencode/test/provider/provider.test.ts`, the file intentionally uses AppRuntime-based async helpers (`run`, `list`, `getProvider`, etc.) rather than `testEffect(...)` for all tests. Converting individual tests to `testEffect` while leaving the rest on the async pattern would create internal inconsistency. A full harness migration of this file is the right approach if the pattern needs to change, but that should be a separate PR.
Applied to files:
packages/opencode/test/util/effect-zod.test.ts
📚 Learning: 2026-04-27T11:18:47.298Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch.test.ts:21-78
Timestamp: 2026-04-27T11:18:47.298Z
Learning: In `packages/opencode/test/tool/websearch.test.ts`, the tests intentionally use manual `Effect.runPromise` with explicit `Effect.provide(...)` chains (including `Layer.succeed(Auth.Service, ...)`, `Layer.succeed(HttpClient.HttpClient, http)`, `WebSearchAuth.layer`, `Truncate.defaultLayer`, and `Agent.defaultLayer`) rather than the `testEffect(...)` harness. This is by design: the fake Auth and HTTP recovery-metadata layers must be explicitly injected and kept visible/scoped at the test site. Do NOT suggest migrating these tests to `testEffect` or removing the manual layer provides.
Applied to files:
packages/opencode/test/util/effect-zod.test.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/packages/app/src/testing/**/*.ts : Test-only hooks must be inert unless explicitly enabled and should not add normal-runtime listeners, reactive subscriptions, or per-update allocations
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/session/session.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.TaggedErrorClass` for typed errors in Effect schemas
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/config.tspackages/opencode/src/control-plane/schema.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.gen(function* () { ... })` for Effect composition
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-23T08:51:00.819Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 186
File: packages/opencode/test/plugin/workspace-adaptor.test.ts:139-144
Timestamp: 2026-04-23T08:51:00.819Z
Learning: For pawwork tests under packages/opencode/test/**, auth.json teardown may intentionally combine `Filesystem.write` (from `packages/opencode/src/util/filesystem.ts`) with `node:fs/promises` `unlink` for cleanup. Do not flag this as inconsistent style; it is the established/intentional pattern because `Filesystem` does not provide a `remove`/`unlink` helper.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-21T16:57:25.580Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/agent.ts:108-119
Timestamp: 2026-04-21T16:57:25.580Z
Learning: In `packages/opencode/src/config/agent.ts` (Astro-Han/pawwork), `ConfigPermission.Info` only accepts permission objects or the three action strings `"ask"`, `"allow"`, `"deny"`, and transforms those action strings into `{ "*": action }` before `normalize()` runs. By the time `normalize()` is reached, `configuredPermission` is always either `undefined` or a `Record<string, Rule>` — never a raw arbitrary string. The `Object.assign(permission, configuredPermission)` pattern is therefore safe. Do not flag it as corrupting string permission references.
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/permission/schema.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-27T11:18:47.596Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/agent/agent.test.ts:440-447
Timestamp: 2026-04-27T11:18:47.596Z
Learning: In `packages/opencode/test/agent/agent.test.ts` (Astro-Han/pawwork), all agent-permission tests intentionally use the manual `tmpdir()` + `Instance.provide(...)` pattern. Do not flag individual tests in this file for conversion to `provideTmpdirInstance(...)` or `provideInstance(...)`; a full harness migration would be a separate PR if the pattern ever needs to change.
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Prefer `Path.Path`, `Config`, `Clock`, and `DateTime` services when those concerns are already inside Effect code
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Prefer `FileSystem.FileSystem` instead of raw `fs/promises` for effectful file I/O in Effect services
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.fn("Domain.method")` for named/traced effects and `Effect.fnUntraced` for internal helpers; these accept pipeable operators as extra arguments to avoid unnecessary outer `.pipe()` wrappers
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-21T16:57:20.257Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:20.257Z
Learning: In packages/opencode/src/config/ config modules, treat both `Schema.Struct` and `Schema.Class` as valid schema patterns and do not require converting `Schema.Struct` to `Schema.Class`. If `Schema.Struct` is used in a config schema (e.g., in files like `command.ts`, `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`), accept it as intentional; the selection should be based on upstream alignment and the local schema context, not on a rule that one form must replace the other.
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-27T08:58:00.665Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:531-538
Timestamp: 2026-04-27T08:58:00.665Z
Learning: When using Effect (e.g., `yield*` with `Effect`-style generator yielding), only use `yield* new SomeErrorClass(...)` if `SomeErrorClass` extends `Schema.TaggedErrorClass` (i.e., it implements Effect’s Yieldable interface). For plain `Error` subclasses (like `BlockedLoopError` / `LoopStopError`) or inline `new Error(...)` values, they are not yieldable and must be wrapped as `yield* Effect.fail(new PlainError(...))`. Do not recommend changing `yield* Effect.fail(new SomePlainError(...))` to `yield* new SomePlainError(...)` unless the error class extends `Schema.TaggedErrorClass`.
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/tool/edit.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/session/projectors.ts
📚 Learning: 2026-04-27T12:59:45.694Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/prompt-effect.test.ts:0-0
Timestamp: 2026-04-27T12:59:45.694Z
Learning: In session prompt/diagnostics tests (e.g., when verifying injected LLM “recovery reminder” copy), do not assert a single exact reminder phrasing tied to signature kind. If a scenario can produce both `input:` and `target:` signatures (for example, a `read` tool call with a `filePath` parameter), accept either phrasing: the input-repeat variant may say “repeated the same tool input 3 times” (literal count), and the target-repeat variant may say “failed against the same target multiple times” (“multiple times” without a count). Your assertions should allow both variants rather than narrowing to only the input-variant text.
Applied to files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.ts
📚 Learning: 2026-04-26T16:34:57.130Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 247
File: packages/ui/src/components/message-part.tsx:1322-1324
Timestamp: 2026-04-26T16:34:57.130Z
Learning: In Astro-Han/pawwork (`packages/ui/src/components/message-part.tsx`), the `taskId` createMemo and `childSessionId` createMemo both intentionally read only from `partMetadata().sessionId` (populated post-execution), not from `input.task_id` / `input.subagent_session_id`. This has always been the case — the original code never read the input field either. Adding an `input.subagent_session_id` fallback would be a new capability, not a bug fix. Do NOT flag the absence of this fallback as a regression in PR `#247` or future PRs unless there is a concrete case where metadata is not populated.
Applied to files:
packages/opencode/test/session/session.test.ts
📚 Learning: 2026-04-26T15:35:36.505Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 245
File: packages/opencode/src/question/index.ts:21-24
Timestamp: 2026-04-26T15:35:36.505Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/question/index.ts`), `Question.Option.description` is intentionally **required** (`z.string()`, not `.optional()`). This matches the upstream opencode contract and all current callers (e.g. `PlanExitTool`) always supply a description. The defensive `<Show when={props.description}>` rendering in `session-question-dock.tsx` is a standard guard, not a signal that the field is intended to be optional. Do NOT suggest making `Option.description` optional without a dedicated follow-up that covers schema + tool description + dock copy + tests.
Applied to files:
packages/opencode/src/question/schema.tspackages/opencode/src/tool/todo.ts
📚 Learning: 2026-04-27T10:32:59.274Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/diagnostics.ts:245-252
Timestamp: 2026-04-27T10:32:59.274Z
Learning: In pawwork’s session diagnostics/loop error handling (e.g., packages/opencode/src/session/diagnostics.ts), keep `loopLastError` stored as raw/unscrubbed error text in loop metadata. The intended trust boundary for sensitive-data scrubbing is the renderer: `LoopRenderer.render` must apply `scrubErrorText` before any user/model-facing output. Do not recommend scrubbing `loopLastError` at the `observeToolError` call site, because failed tool parts already store raw errors in `state.error` (pre-existing leak) and scrubbing only `loopLastError` would be lossy without closing the gap. If export-side hardening is required, implement a single sanitizer pass in the export layer over both `state.error` and the loop/metadata fields, rather than sanitizing only `loopLastError` earlier in the storage path.
Applied to files:
packages/opencode/src/session/schema.tspackages/opencode/src/session/projectors.ts
📚 Learning: 2026-04-27T10:33:08.974Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:08.974Z
Learning: For Astro-Han/pawwork session processing code under packages/opencode/src/session/ (e.g., prompt.ts and processor.ts), this specific loop-gate TOCTOU/race between buildLoopContext() and recordSyntheticBlock/recordSyntheticStop is intentionally handled via idempotence guards (re-check sigKey presence and hasStopped inside the record helpers). Do not treat the absence of a per-parent Effect.Mutex as a blocking review issue for this known edge case; the residual window should only create extra synthetic parts and must not change the "turn ends" contract. Respect the existing code comment documenting the trade-off, and only escalate to a full-mutex approach if the race is observed in practice.
Applied to files:
packages/opencode/src/session/schema.tspackages/opencode/src/session/projectors.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.cached` when multiple concurrent callers should share a single in-flight computation rather than storing `Fiber | undefined` or `Promise | undefined` manually
Applied to files:
packages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `InstanceState` from `src/effect/instance-state.ts` for per-directory or per-project state that needs per-instance cleanup; do work directly in the `InstanceState.make` closure where `ScopedCache` handles run-once semantics
Applied to files:
packages/opencode/src/config/config.ts
📚 Learning: 2026-04-24T06:50:05.142Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 211
File: packages/opencode/src/provider/models.ts:113-179
Timestamp: 2026-04-24T06:50:05.142Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/provider/models.ts`), `PublishModel` and `PublishProvider` intentionally duplicate fields from `Model` and `Provider` with looser optionality. The split is a deliberate behavior boundary: `PublishModel`/`PublishProvider` (with `.passthrough()`) are used for lenient publish-time validation of incoming catalogs, while `Model`/`Provider` are used for strict runtime normalization. Do not flag this duplication as a maintenance burden or suggest extracting a shared base schema — the explicit separation is intentional and tied to the models-refresh reliability path introduced in PR `#211`.
Applied to files:
packages/opencode/src/control-plane/schema.ts
📚 Learning: 2026-04-22T08:49:47.800Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/desktop-electron/src/main/index-sidecar-source.test.ts:3-11
Timestamp: 2026-04-22T08:49:47.800Z
Learning: In `packages/desktop-electron/src/main/index-sidecar-source.test.ts` (Astro-Han/pawwork), the test intentionally uses `expect(source).toContain` / `expect(source).not.toContain` string matching against the raw `index.ts` source text as a lightweight sidecar contract guard. The maintainer has explicitly chosen not to introduce an AST parser (e.g., `babel/parser` or acorn) for this purpose. Do not flag these string-based assertions as fragile or suggest converting them to AST-based matching.
Applied to files:
packages/opencode/test/config/config.test.ts
🔇 Additional comments (16)
packages/opencode/src/tool/edit.ts (1)
149-159: Good simplification of diff stat computation.Single-pass accumulation is cleaner and avoids mutating
filedifffields in a follow-up pass.packages/opencode/src/tool/todo.ts (1)
7-21: Inline todo parameter shape looks correct and type-safe for tool execution.The updated
parametersshape is consistent with the expected todo payload and keepsexecuteinference explicit.packages/opencode/src/util/effect-zod.ts (1)
62-70: Walker layering refactor is clean.Applying descriptions/refs after base derivation and centralizing checks in
bodyWithChecksimproves consistency without changing behavior intent.Also applies to: 72-87
packages/opencode/test/util/effect-zod.test.ts (1)
5-5: Test import update matches the schema adapter surface.Removing
ZodPreprocessfrom imports is consistent with the runtime removal and keeps the test module aligned.packages/opencode/src/project/schema.ts (1)
3-3: ProjectID migration to sharedzod(schema)adapter looks good.This keeps the branded Effect schema as source-of-truth while preserving the static Zod accessor.
Also applies to: 13-13
packages/opencode/src/session/schema.ts (1)
4-4: Session identifier schema migration is consistent and safe.Using
zod(s)withZodOverridekeeps the existing ID validation semantics while centralizing derivation.Also applies to: 11-11, 21-21, 31-31
packages/opencode/src/tool/schema.ts (1)
4-4: ToolID schema update is aligned with the migration pattern.The switch to shared
zod(schema)keeps behavior centralized without changing the brand/override contract.Also applies to: 14-14
packages/opencode/src/pty/schema.ts (1)
4-4: PtyID migration is coherent with the shared effect-zod approach.The new override +
zod(schema)pairing preserves ID validation intent while reducing duplicated Zod wiring.Also applies to: 7-7, 14-14
packages/opencode/src/control-plane/schema.ts (1)
4-8: LGTM:WorkspaceIDnow cleanly uses the shared Effect→Zod bridge.The override annotation plus
zod(schema)static keeps validation behavior aligned while preserving branded typing.Also applies to: 16-16
packages/opencode/src/question/schema.ts (1)
4-4: LGTM:QuestionIDmigration is consistent and preserves schema intent.Using
ZodOverridewithzod(this)keeps the Newtype contract and removes manual cast-based Zod plumbing.Also applies to: 7-10, 15-15
packages/opencode/src/sync/schema.ts (1)
4-4: LGTM:EventIDnow follows the centralized override + derive pattern.The change is coherent with the rest of the ID schema migrations.
Also applies to: 7-7, 11-11
packages/opencode/src/permission/schema.ts (1)
4-4: LGTM:PermissionIDmigration is correct and consistent with adapter-driven Zod generation.Also applies to: 7-10, 15-15
packages/opencode/script/schema.ts (1)
56-56: LGTM: schema emission now targetsConfig.Info.zod, matching the migrated validation surface.packages/opencode/test/session/compaction.test.ts (1)
178-178: LGTM: test baseline config now correctly usesConfig.Info.zod.parse({}).packages/opencode/test/config/config.test.ts (1)
2950-2950: LGTM: managed-plist parsing tests are consistently updated toConfig.Info.zod.This keeps test validation aligned with the new schema surface.
Also applies to: 2982-2982, 3002-3002, 3032-3032, 3049-3049
packages/opencode/src/server/instance/config.ts (1)
26-26: LGTM: config routes now consistently useConfig.Info.zodfor both OpenAPI schemas and request validation.Also applies to: 47-47, 54-54
0356b26 to
205cdf4
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/opencode/test/config/config.test.ts (1)
2116-2135: 🛠️ Refactor suggestion | 🟠 MajorUse
tmpdir({ config: ... })instead of manual config file writes in this test setup.This case manually writes
opencode.jsonininit; please use the fixture’sconfigoption for consistency with test helpers.♻️ Suggested refactor
- await using tmp = await tmpdir({ - init: async (dir) => { - await Filesystem.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - permission: { - "*": "deny", - edit: "ask", - write: "ask", - external_directory: "ask", - read: "allow", - todowrite: "allow", - "thoughts_*": "allow", - "reasoning_model_*": "allow", - "tools_*": "allow", - "pr_comments_*": "allow", - }, - }), - ) - }, - }) + await using tmp = await tmpdir({ + config: { + permission: { + "*": "deny", + edit: "ask", + write: "ask", + external_directory: "ask", + read: "allow", + todowrite: "allow", + "thoughts_*": "allow", + "reasoning_model_*": "allow", + "tools_*": "allow", + "pr_comments_*": "allow", + }, + }, + })As per coding guidelines: Use the
configoption intmpdirto write anopencode.jsonconfig file during test setup by passing a partial Config.Info object.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/opencode/test/config/config.test.ts` around lines 2116 - 2135, Replace the manual opencode.json write inside the tmpdir init callback by using the tmpdir fixture's config option: remove the Filesystem.write call that writes "opencode.json" and instead pass a partial Config.Info object to tmpdir({ config: { ... } }); locate the test's tmpdir invocation and the init async (dir) => { ... } block and move the JSON object (schema and permission map) into the tmpdir call as its config property so the fixture creates opencode.json automatically.
♻️ Duplicate comments (1)
packages/opencode/test/session/session.test.ts (1)
123-129:⚠️ Potential issue | 🟡 MinorStill narrow this subscription before assigning
received.The cast only fixes the readonly-vs-mutable type mismatch; it does not stop an unrelated
PartUpdatedevent from overwritingreceivedbefore the assertions. Please keep the runtime discriminator guard from the earlier review.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/opencode/test/session/session.test.ts` around lines 123 - 129, The subscriber assigned to Bus.subscribe(MessageV2.Event.PartUpdated) currently casts and writes to received directly, allowing unrelated PartUpdated events to overwrite it; update the callback in Bus.subscribe so it first performs a runtime discriminator guard (e.g., check event.properties.part.id or another expected discriminant) before assigning to the local received variable, and keep the existing unsubscribe logic (unsub) after the assertion to avoid leaks; reference the MessageV2.Event.PartUpdated subscription, the event.properties.part payload, the received variable, and the unsub cleanup when making the change.
🤖 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/opencode/src/tool/todo.ts`:
- Around line 7-22: Replace the inline todo schema in the parameters constant
with the canonical Todo.Info to avoid duplication: confirm that Tool.define
still infers execute() when using z.object({ todos: z.array(Todo.Info) }) (or
z.array(Todo.Info) if parameters shape differs), and if inference fails, adjust
by wrapping Todo.Info in z.array(...) while keeping parameters typed as the same
constant name (parameters) so Tool.define and execute() continue to work; update
imports to reference Todo from packages/opencode/src/session/todo.ts and run
TypeScript checks to ensure no inference/type errors.
---
Outside diff comments:
In `@packages/opencode/test/config/config.test.ts`:
- Around line 2116-2135: Replace the manual opencode.json write inside the
tmpdir init callback by using the tmpdir fixture's config option: remove the
Filesystem.write call that writes "opencode.json" and instead pass a partial
Config.Info object to tmpdir({ config: { ... } }); locate the test's tmpdir
invocation and the init async (dir) => { ... } block and move the JSON object
(schema and permission map) into the tmpdir call as its config property so the
fixture creates opencode.json automatically.
---
Duplicate comments:
In `@packages/opencode/test/session/session.test.ts`:
- Around line 123-129: The subscriber assigned to
Bus.subscribe(MessageV2.Event.PartUpdated) currently casts and writes to
received directly, allowing unrelated PartUpdated events to overwrite it; update
the callback in Bus.subscribe so it first performs a runtime discriminator guard
(e.g., check event.properties.part.id or another expected discriminant) before
assigning to the local received variable, and keep the existing unsubscribe
logic (unsub) after the assertion to avoid leaks; reference the
MessageV2.Event.PartUpdated subscription, the event.properties.part payload, the
received variable, and the unsub cleanup when making the change.
🪄 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: b2b8e79a-702d-4135-9486-3976088cfb86
⛔ Files ignored due to path filters (1)
packages/sdk/js/src/v2/gen/types.gen.tsis excluded by!**/gen/**
📒 Files selected for processing (23)
packages/opencode/script/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/permission/index.tspackages/opencode/src/permission/schema.tspackages/opencode/src/project/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/session/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/tool/edit.tspackages/opencode/src/tool/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/test/config/config.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: smoke-macos-arm64
- GitHub Check: unit-windows-opencode-server-tools
- GitHub Check: unit-windows-app
- GitHub Check: unit-windows-opencode-session
- GitHub Check: unit-windows-opencode-config-project
- GitHub Check: unit-windows-desktop
- GitHub Check: unit-app
- GitHub Check: unit-desktop
- GitHub Check: unit-opencode
- GitHub Check: e2e-artifacts
- GitHub Check: analyze-js-ts
🧰 Additional context used
📓 Path-based instructions (2)
packages/opencode/**/*.ts
📄 CodeRabbit inference engine (packages/opencode/AGENTS.md)
packages/opencode/**/*.ts: UseEffect.gen(function* () { ... })for Effect composition
UseEffect.fn("Domain.method")for named/traced effects andEffect.fnUntracedfor internal helpers; these accept pipeable operators as extra arguments to avoid unnecessary outer.pipe()wrappers
UseEffect.callbackfor callback-based APIs
PreferDateTime.nowAsDateovernew Date(yield* Clock.currentTimeMillis)when you need aDatein Effect code
UseSchema.Classfor multi-field data in Effect schemas
Use branded schemas (Schema.brand) for single-value types in Effect
UseSchema.TaggedErrorClassfor typed errors in Effect schemas
UseSchema.Defectinstead ofunknownfor defect-like causes in Effect code
InEffect.gen/Effect.fn, preferyield* new MyError(...)overyield* Effect.fail(new MyError(...))for direct early-failure branches
UsemakeRuntimefromsrc/effect/run-service.tsfor all services; it returns{ runPromise, runFork, runCallback }backed by a sharedmemoMapthat deduplicates layers
UseInstanceStatefromsrc/effect/instance-state.tsfor per-directory or per-project state that needs per-instance cleanup; do work directly in theInstanceState.makeclosure whereScopedCachehandles run-once semantics
UseEffect.addFinalizerorEffect.acquireReleaseinside theInstanceState.makeclosure for cleanup (subscriptions, process teardown, etc.)
UseEffect.forkScopedinside theInstanceState.makeclosure for background stream consumers — the fiber is interrupted when the instance is disposed
PreferFileSystem.FileSysteminstead of rawfs/promisesfor effectful file I/O in Effect services
PreferChildProcessSpawner.ChildProcessSpawnerwithChildProcess.make(...)instead of custom process wrappers in Effect services
PreferHttpClient.HttpClientinstead of rawfetchin Effect services
PreferPath.Path,Config,Clock, andDateTimeservices when those concerns are already inside Effect code
For backgroun...
Files:
packages/opencode/script/schema.tspackages/opencode/src/permission/index.tspackages/opencode/src/server/instance/config.tspackages/opencode/test/session/session.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/edit.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/pty/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/test/session/compaction.test.tspackages/opencode/src/tool/todo.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.tspackages/opencode/test/config/config.test.tspackages/opencode/src/config/config.ts
packages/opencode/test/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (packages/opencode/test/AGENTS.md)
packages/opencode/test/**/*.test.{ts,tsx}: Use thetmpdirfunction fromfixture/fixture.tsto create temporary directories for tests with automatic cleanup. Useawait usingsyntax to ensure automatic cleanup when the variable goes out of scope.
When using thetmpdirfunction with git repository support, pass thegit: trueoption to initialize a git repo with a root commit.
Use theconfigoption intmpdirto write anopencode.jsonconfig file during test setup by passing a partial Config.Info object.
Use theinitoption intmpdirto define custom setup functions that can return extra data accessible viatmp.extra, and use thedisposeoption for custom cleanup logic.
UsetestEffect(...)fromtest/lib/effect.tsfor tests that exercise Effect services or Effect-based workflows.
Useit.effect(...)when the test should run withTestClockandTestConsole. Useit.live(...)when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.
Prefer Effect-aware helpers fromfixture/fixture.tsover building manual runtimes in tests: usetmpdirScoped()for scoped temp directories,provideInstance(dir)(effect)for low-level binding without directory creation,provideTmpdirInstance(...)for single temp instance binding, orprovideTmpdirServer(...)for tests that also need the test LLM server.
Defineconst it = testEffect(...)near the top of the test file and keep the test body insideEffect.gen(function* () { ... }). Yield services directly withyield* MyService.Serviceoryield* MyTool.
Avoid customManagedRuntime,attach(...), or ad hocrun(...)wrappers in Effect tests whentestEffect(...)already provides the runtime.
When a test needs instance-local state, preferprovideTmpdirInstance(...)orprovideInstance(...)over manualInstance.provide(...)inside Promise-style tests.
Files:
packages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/config/config.test.ts
🧠 Learnings (41)
📓 Common learnings
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/opencode/test/provider/provider.test.ts:64-85
Timestamp: 2026-04-22T09:32:54.556Z
Learning: In `packages/opencode/test/provider/provider.test.ts`, the file intentionally uses AppRuntime-based async helpers (`run`, `list`, `getProvider`, etc.) rather than `testEffect(...)` for all tests. Converting individual tests to `testEffect` while leaving the rest on the async pattern would create internal inconsistency. A full harness migration of this file is the right approach if the pattern needs to change, but that should be a separate PR.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch-auth.test.ts:0-0
Timestamp: 2026-04-27T11:19:24.963Z
Learning: In `packages/opencode/test/tool/websearch-auth.test.ts` (Astro-Han/pawwork), the tests intentionally use a small local `runWith` runner with raw `bun:test` and `Effect.runPromise` rather than the `testEffect` harness. Each test case injects a custom in-memory `Auth.Service` layer; switching to `testEffect` would be style-only churn without changing risk coverage. Do not flag these tests as needing harness migration.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/mcp-exa.test.ts:1-186
Timestamp: 2026-04-27T11:18:47.332Z
Learning: In `packages/opencode/test/tool/mcp-exa.test.ts` (Astro-Han/pawwork), the tests intentionally use raw `bun:test` async cases with `Effect.runPromise(...)` and per-case `HttpClient.make(...)` fakes rather than the `testEffect(...)` harness. The maintainer has explicitly decided not to migrate, because the HttpClient fake wiring is itself the behavior under test and switching to `testEffect` would be style churn without changing risk coverage. Do not flag these tests as needing harness migration.
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use branded schemas (`Schema.brand`) for single-value types in Effect
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 211
File: packages/opencode/src/provider/models.ts:113-179
Timestamp: 2026-04-24T06:50:05.142Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/provider/models.ts`), `PublishModel` and `PublishProvider` intentionally duplicate fields from `Model` and `Provider` with looser optionality. The split is a deliberate behavior boundary: `PublishModel`/`PublishProvider` (with `.passthrough()`) are used for lenient publish-time validation of incoming catalogs, while `Model`/`Provider` are used for strict runtime normalization. Do not flag this duplication as a maintenance burden or suggest extracting a shared base schema — the explicit separation is intentional and tied to the models-refresh reliability path introduced in PR `#211`.
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Class` for multi-field data in Effect schemas
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Defect` instead of `unknown` for defect-like causes in Effect code
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:58.310Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:12.228Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/prompt.ts` and `packages/opencode/src/session/processor.ts`, PR `#264`), the loop-gate race condition between `buildLoopContext()` and `recordSyntheticBlock`/`recordSyntheticStop` is intentionally handled via idempotence guards (re-check sigKey presence / `hasStopped` inside the record helpers) rather than a full per-parent `Effect.Mutex`. Threading a `Map<MessageID, Mutex>` through the processor was considered too large a surface change for this edge case; the residual TOCTOU window only produces extra synthetic parts with no behavioral drift on the "turn ends" contract. A code comment documents the trade-off and points to a full-mutex follow-up if the race is observed in practice. Do NOT re-flag the absence of a per-parent mutex as a blocking issue in future reviews.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/session/projectors.ts:0-0
Timestamp: 2026-04-28T01:24:29.645Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/projectors.ts`), `SyncEvent.Definition.schema` preserves the literal Zod type through `z.infer<Def["schema"]>`, so `data.info` inside a `SyncEvent.project(Session.Event.Created, ...)` callback is already typed as `Session.Info` by the TypeScript compiler. No `as Session.Info` cast or `Session.Info.parse()` call is needed at this persistence boundary — a typecheck-clean removal of any such cast is sufficient.
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.TaggedErrorClass` for typed errors in Effect schemas
📚 Learning: 2026-04-28T01:24:29.645Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/session/projectors.ts:0-0
Timestamp: 2026-04-28T01:24:29.645Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/projectors.ts`), `SyncEvent.Definition.schema` preserves the literal Zod type through `z.infer<Def["schema"]>`, so `data.info` inside a `SyncEvent.project(Session.Event.Created, ...)` callback is already typed as `Session.Info` by the TypeScript compiler. No `as Session.Info` cast or `Session.Info.parse()` call is needed at this persistence boundary — a typecheck-clean removal of any such cast is sufficient.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/test/session/session.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/project/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/pty/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-21T16:57:23.748Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:23.748Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/config/`), `Schema.Struct` and `Schema.Class` are both valid and intentionally used patterns across config modules (e.g. `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`, `command.ts`). Do not flag `Schema.Struct` usage in config schemas as needing conversion to `Schema.Class` — the choice is driven by upstream alignment and local context, not inconsistency.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use branded schemas (`Schema.brand`) for single-value types in Effect
Applied to files:
packages/opencode/script/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/project/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/pty/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Class` for multi-field data in Effect schemas
Applied to files:
packages/opencode/script/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/project/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/pty/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-21T16:57:25.580Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/agent.ts:108-119
Timestamp: 2026-04-21T16:57:25.580Z
Learning: In `packages/opencode/src/config/agent.ts` (Astro-Han/pawwork), `ConfigPermission.Info` only accepts permission objects or the three action strings `"ask"`, `"allow"`, `"deny"`, and transforms those action strings into `{ "*": action }` before `normalize()` runs. By the time `normalize()` is reached, `configuredPermission` is always either `undefined` or a `Record<string, Rule>` — never a raw arbitrary string. The `Object.assign(permission, configuredPermission)` pattern is therefore safe. Do not flag it as corrupting string permission references.
Applied to files:
packages/opencode/src/permission/index.tspackages/opencode/src/config/agent.tspackages/opencode/src/permission/schema.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/permission.tspackages/opencode/test/config/config.test.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-27T08:58:00.665Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:531-538
Timestamp: 2026-04-27T08:58:00.665Z
Learning: When using Effect (e.g., `yield*` with `Effect`-style generator yielding), only use `yield* new SomeErrorClass(...)` if `SomeErrorClass` extends `Schema.TaggedErrorClass` (i.e., it implements Effect’s Yieldable interface). For plain `Error` subclasses (like `BlockedLoopError` / `LoopStopError`) or inline `new Error(...)` values, they are not yieldable and must be wrapped as `yield* Effect.fail(new PlainError(...))`. Do not recommend changing `yield* Effect.fail(new SomePlainError(...))` to `yield* new SomePlainError(...)` unless the error class extends `Schema.TaggedErrorClass`.
Applied to files:
packages/opencode/src/permission/index.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/config/agent.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/edit.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/pty/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-26T16:34:57.130Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 247
File: packages/ui/src/components/message-part.tsx:1322-1324
Timestamp: 2026-04-26T16:34:57.130Z
Learning: In Astro-Han/pawwork (`packages/ui/src/components/message-part.tsx`), the `taskId` createMemo and `childSessionId` createMemo both intentionally read only from `partMetadata().sessionId` (populated post-execution), not from `input.task_id` / `input.subagent_session_id`. This has always been the case — the original code never read the input field either. Adding an `input.subagent_session_id` fallback would be a new capability, not a bug fix. Do NOT flag the absence of this fallback as a regression in PR `#247` or future PRs unless there is a concrete case where metadata is not populated.
Applied to files:
packages/opencode/test/session/session.test.ts
📚 Learning: 2026-04-20T14:36:04.113Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/app/e2e/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:04.113Z
Learning: Applies to packages/app/e2e/packages/app/src/testing/**/*.ts : Test-only hooks must be inert unless explicitly enabled and should not add normal-runtime listeners, reactive subscriptions, or per-update allocations
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.ts
📚 Learning: 2026-04-22T08:49:47.800Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/desktop-electron/src/main/index-sidecar-source.test.ts:3-11
Timestamp: 2026-04-22T08:49:47.800Z
Learning: In `packages/desktop-electron/src/main/index-sidecar-source.test.ts` (Astro-Han/pawwork), the test intentionally uses `expect(source).toContain` / `expect(source).not.toContain` string matching against the raw `index.ts` source text as a lightweight sidecar contract guard. The maintainer has explicitly chosen not to introduce an AST parser (e.g., `babel/parser` or acorn) for this purpose. Do not flag these string-based assertions as fragile or suggest converting them to AST-based matching.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-27T11:18:47.332Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/mcp-exa.test.ts:1-186
Timestamp: 2026-04-27T11:18:47.332Z
Learning: In `packages/opencode/test/tool/mcp-exa.test.ts` (Astro-Han/pawwork), the tests intentionally use raw `bun:test` async cases with `Effect.runPromise(...)` and per-case `HttpClient.make(...)` fakes rather than the `testEffect(...)` harness. The maintainer has explicitly decided not to migrate, because the HttpClient fake wiring is itself the behavior under test and switching to `testEffect` would be style churn without changing risk coverage. Do not flag these tests as needing harness migration.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/permission/next.test.ts
📚 Learning: 2026-04-27T10:33:12.228Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:12.228Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/prompt.ts` and `packages/opencode/src/session/processor.ts`, PR `#264`), the loop-gate race condition between `buildLoopContext()` and `recordSyntheticBlock`/`recordSyntheticStop` is intentionally handled via idempotence guards (re-check sigKey presence / `hasStopped` inside the record helpers) rather than a full per-parent `Effect.Mutex`. Threading a `Map<MessageID, Mutex>` through the processor was considered too large a surface change for this edge case; the residual TOCTOU window only produces extra synthetic parts with no behavioral drift on the "turn ends" contract. A code comment documents the trade-off and points to a full-mutex follow-up if the race is observed in practice. Do NOT re-flag the absence of a per-parent mutex as a blocking issue in future reviews.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T12:59:45.694Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/prompt-effect.test.ts:0-0
Timestamp: 2026-04-27T12:59:45.694Z
Learning: In session prompt/diagnostics tests (e.g., when verifying injected LLM “recovery reminder” copy), do not assert a single exact reminder phrasing tied to signature kind. If a scenario can produce both `input:` and `target:` signatures (for example, a `read` tool call with a `filePath` parameter), accept either phrasing: the input-repeat variant may say “repeated the same tool input 3 times” (literal count), and the target-repeat variant may say “failed against the same target multiple times” (“multiple times” without a count). Your assertions should allow both variants rather than narrowing to only the input-variant text.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/session/compaction.test.ts
📚 Learning: 2026-04-27T10:08:57.448Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/diagnostics.test.ts:313-318
Timestamp: 2026-04-27T10:08:57.448Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/diagnostics.ts`, PR `#264`), `SessionDiagnostics.LoopAction` is a TypeScript string literal union type (`"observe" | "block" | "stop"`), NOT a runtime enum. There is no runtime form to introspect via `Object.values()`. The test pattern `const all: SessionDiagnostics.LoopAction[] = ["observe", "block", "stop"]; expect(all).toHaveLength(3)` is intentional and correct — it provides compile-time exhaustiveness via the type annotation. Do NOT suggest replacing this with `Object.values()` introspection or flag the runtime length check as insufficient.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-24T06:50:05.142Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 211
File: packages/opencode/src/provider/models.ts:113-179
Timestamp: 2026-04-24T06:50:05.142Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/provider/models.ts`), `PublishModel` and `PublishProvider` intentionally duplicate fields from `Model` and `Provider` with looser optionality. The split is a deliberate behavior boundary: `PublishModel`/`PublishProvider` (with `.passthrough()`) are used for lenient publish-time validation of incoming catalogs, while `Model`/`Provider` are used for strict runtime normalization. Do not flag this duplication as a maintenance burden or suggest extracting a shared base schema — the explicit separation is intentional and tied to the models-refresh reliability path introduced in PR `#211`.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/control-plane/schema.ts
📚 Learning: 2026-04-25T12:52:39.403Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 234
File: packages/opencode/src/session/export.ts:75-88
Timestamp: 2026-04-25T12:52:39.403Z
Learning: In `packages/opencode/src/session/export.ts` (Astro-Han/pawwork), the `extractReasonFromCause` function intentionally uses a lightweight cast-based inspection of the Cause `reasons` array instead of `Cause.failures()` / `Cause.defects()` utilities. This is a deliberate choice: the function is diagnostic-only (used to populate a best-effort reason string for export diagnostics), and the maintainer explicitly prefers not to add coupling to Effect's Cause API surface for this purpose. Do not flag this pattern as fragile or suggest replacing it with Cause utilities.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-23T08:51:00.819Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 186
File: packages/opencode/test/plugin/workspace-adaptor.test.ts:139-144
Timestamp: 2026-04-23T08:51:00.819Z
Learning: For pawwork tests under packages/opencode/test/**, auth.json teardown may intentionally combine `Filesystem.write` (from `packages/opencode/src/util/filesystem.ts`) with `node:fs/promises` `unlink` for cleanup. Do not flag this as inconsistent style; it is the established/intentional pattern because `Filesystem` does not provide a `remove`/`unlink` helper.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Defect` instead of `unknown` for defect-like causes in Effect code
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/project/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/pty/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-27T11:18:47.596Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/agent/agent.test.ts:440-447
Timestamp: 2026-04-27T11:18:47.596Z
Learning: In `packages/opencode/test/agent/agent.test.ts` (Astro-Han/pawwork), all agent-permission tests intentionally use the manual `tmpdir()` + `Instance.provide(...)` pattern. Do not flag individual tests in this file for conversion to `provideTmpdirInstance(...)` or `provideInstance(...)`; a full harness migration would be a separate PR if the pattern ever needs to change.
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.TaggedErrorClass` for typed errors in Effect schemas
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/project/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/pty/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Prefer `Path.Path`, `Config`, `Clock`, and `DateTime` services when those concerns are already inside Effect code
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Prefer `FileSystem.FileSystem` instead of raw `fs/promises` for effectful file I/O in Effect services
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.fn("Domain.method")` for named/traced effects and `Effect.fnUntraced` for internal helpers; these accept pipeable operators as extra arguments to avoid unnecessary outer `.pipe()` wrappers
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.gen(function* () { ... })` for Effect composition
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Define `const it = testEffect(...)` near the top of the test file and keep the test body inside `Effect.gen(function* () { ... })`. Yield services directly with `yield* MyService.Service` or `yield* MyTool`.
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-21T16:57:20.257Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:20.257Z
Learning: In packages/opencode/src/config/ config modules, treat both `Schema.Struct` and `Schema.Class` as valid schema patterns and do not require converting `Schema.Struct` to `Schema.Class`. If `Schema.Struct` is used in a config schema (e.g., in files like `command.ts`, `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`), accept it as intentional; the selection should be based on upstream alignment and the local schema context, not on a rule that one form must replace the other.
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `testEffect(...)` from `test/lib/effect.ts` for tests that exercise Effect services or Effect-based workflows.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-27T11:19:24.963Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch-auth.test.ts:0-0
Timestamp: 2026-04-27T11:19:24.963Z
Learning: In `packages/opencode/test/tool/websearch-auth.test.ts` (Astro-Han/pawwork), the tests intentionally use a small local `runWith` runner with raw `bun:test` and `Effect.runPromise` rather than the `testEffect` harness. Each test case injects a custom in-memory `Auth.Service` layer; switching to `testEffect` would be style-only churn without changing risk coverage. Do not flag these tests as needing harness migration.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/permission/next.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` wrappers in Effect tests when `testEffect(...)` already provides the runtime.
Applied to files:
packages/opencode/test/util/effect-zod.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Prefer Effect-aware helpers from `fixture/fixture.ts` over building manual runtimes in tests: use `tmpdirScoped()` for scoped temp directories, `provideInstance(dir)(effect)` for low-level binding without directory creation, `provideTmpdirInstance(...)` for single temp instance binding, or `provideTmpdirServer(...)` for tests that also need the test LLM server.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-22T09:32:54.556Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/opencode/test/provider/provider.test.ts:64-85
Timestamp: 2026-04-22T09:32:54.556Z
Learning: In `packages/opencode/test/provider/provider.test.ts`, the file intentionally uses AppRuntime-based async helpers (`run`, `list`, `getProvider`, etc.) rather than `testEffect(...)` for all tests. Converting individual tests to `testEffect` while leaving the rest on the async pattern would create internal inconsistency. A full harness migration of this file is the right approach if the pattern needs to change, but that should be a separate PR.
Applied to files:
packages/opencode/test/util/effect-zod.test.ts
📚 Learning: 2026-04-27T11:18:47.298Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch.test.ts:21-78
Timestamp: 2026-04-27T11:18:47.298Z
Learning: In `packages/opencode/test/tool/websearch.test.ts`, the tests intentionally use manual `Effect.runPromise` with explicit `Effect.provide(...)` chains (including `Layer.succeed(Auth.Service, ...)`, `Layer.succeed(HttpClient.HttpClient, http)`, `WebSearchAuth.layer`, `Truncate.defaultLayer`, and `Agent.defaultLayer`) rather than the `testEffect(...)` harness. This is by design: the fake Auth and HTTP recovery-metadata layers must be explicitly injected and kept visible/scoped at the test site. Do NOT suggest migrating these tests to `testEffect` or removing the manual layer provides.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.ts
📚 Learning: 2026-04-27T10:32:59.274Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/diagnostics.ts:245-252
Timestamp: 2026-04-27T10:32:59.274Z
Learning: In pawwork’s session diagnostics/loop error handling (e.g., packages/opencode/src/session/diagnostics.ts), keep `loopLastError` stored as raw/unscrubbed error text in loop metadata. The intended trust boundary for sensitive-data scrubbing is the renderer: `LoopRenderer.render` must apply `scrubErrorText` before any user/model-facing output. Do not recommend scrubbing `loopLastError` at the `observeToolError` call site, because failed tool parts already store raw errors in `state.error` (pre-existing leak) and scrubbing only `loopLastError` would be lossy without closing the gap. If export-side hardening is required, implement a single sanitizer pass in the export layer over both `state.error` and the loop/metadata fields, rather than sanitizing only `loopLastError` earlier in the storage path.
Applied to files:
packages/opencode/src/session/schema.ts
📚 Learning: 2026-04-27T10:33:08.974Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:08.974Z
Learning: For Astro-Han/pawwork session processing code under packages/opencode/src/session/ (e.g., prompt.ts and processor.ts), this specific loop-gate TOCTOU/race between buildLoopContext() and recordSyntheticBlock/recordSyntheticStop is intentionally handled via idempotence guards (re-check sigKey presence and hasStopped inside the record helpers). Do not treat the absence of a per-parent Effect.Mutex as a blocking review issue for this known edge case; the residual window should only create extra synthetic parts and must not change the "turn ends" contract. Respect the existing code comment documenting the trade-off, and only escalate to a full-mutex approach if the race is observed in practice.
Applied to files:
packages/opencode/src/session/schema.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `config` option in `tmpdir` to write an `opencode.json` config file during test setup by passing a partial Config.Info object.
Applied to files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/config/config.test.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `init` option in `tmpdir` to define custom setup functions that can return extra data accessible via `tmp.extra`, and use the `dispose` option for custom cleanup logic.
Applied to files:
packages/opencode/test/session/compaction.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `tmpdir` function from `fixture/fixture.ts` to create temporary directories for tests with automatic cleanup. Use `await using` syntax to ensure automatic cleanup when the variable goes out of scope.
Applied to files:
packages/opencode/test/session/compaction.test.ts
📚 Learning: 2026-04-26T15:35:36.505Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 245
File: packages/opencode/src/question/index.ts:21-24
Timestamp: 2026-04-26T15:35:36.505Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/question/index.ts`), `Question.Option.description` is intentionally **required** (`z.string()`, not `.optional()`). This matches the upstream opencode contract and all current callers (e.g. `PlanExitTool`) always supply a description. The defensive `<Show when={props.description}>` rendering in `session-question-dock.tsx` is a standard guard, not a signal that the field is intended to be optional. Do NOT suggest making `Option.description` optional without a dedicated follow-up that covers schema + tool description + dock copy + tests.
Applied to files:
packages/opencode/src/tool/todo.ts
📚 Learning: 2026-04-27T12:59:49.844Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/prompt-effect.test.ts:0-0
Timestamp: 2026-04-27T12:59:49.844Z
Learning: In `packages/opencode/test/session/prompt-effect.test.ts` and `packages/opencode/src/session/diagnostics.ts` (PR `#264`), the recovery reminder copy differs between signature kinds: the input-repeat variant says "repeated the same tool input 3 times" (uses a literal count), while the target-repeat variant says "failed against the same target multiple times" (uses "multiple times" with no count). Assertions that check for injected reminder text in LLM inputs must accept both phrasings when a scenario produces both `input:` and `target:` signatures (e.g., `read` tool with a `filePath` parameter). Do NOT narrow the assertion to only the input-variant phrasing.
Applied to files:
packages/opencode/test/permission/next.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.cached` when multiple concurrent callers should share a single in-flight computation rather than storing `Fiber | undefined` or `Promise | undefined` manually
Applied to files:
packages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `InstanceState` from `src/effect/instance-state.ts` for per-directory or per-project state that needs per-instance cleanup; do work directly in the `InstanceState.make` closure where `ScopedCache` handles run-once semantics
Applied to files:
packages/opencode/src/config/config.ts
🔇 Additional comments (22)
packages/opencode/src/tool/edit.ts (1)
149-160: Single-pass diff tally looks good.Precomputing
additionsanddeletionshere matches theSnapshot.FileDiffshape and avoids the extra traversal without changing behavior.packages/opencode/src/permission/index.ts (1)
287-312: Good: wildcard precedence is now normalized before evaluation.This keeps the
findLast()-based lookup predictable and preserves the intended “specific rules override wildcard fallbacks” behavior without changing nested rule order.packages/opencode/src/util/effect-zod.ts (2)
43-53: Good: the object helper now validates its contract at runtime.Using
instanceof z.ZodTypeandinstanceof z.ZodObjectkeeps this on public Zod APIs and avoids another private-property escape hatch.
63-89: Good: override, description, and ref metadata are composed cleanly.The walker still applies outer metadata on top of the chosen base schema, and the check handling stays centralized instead of being duplicated across branches.
packages/opencode/src/project/schema.ts (1)
3-14: Good:ProjectID.zodnow derives from the shared adapter.This keeps the static in sync with the Effect schema and removes the local Zod construction path.
packages/opencode/test/util/effect-zod.test.ts (1)
5-765: Good coverage for the new adapter behavior.The suite now exercises the important paths after the refactor: overrides, nested refs, transforms, defaults, refinement translation, and memoization. The dropped preprocess cases are no longer relevant now that support was removed.
packages/opencode/src/tool/schema.ts (1)
4-14: Good:ToolIDnow uses the shared Effect→Zod bridge.This keeps the derived validator aligned with the Effect schema and preserves the override-based runtime pattern.
packages/opencode/src/pty/schema.ts (1)
4-14: Good:PtyIDnow follows the shared schema bridge pattern.The branded Effect schema and its Zod accessor stay aligned, and the override mechanism remains intact.
packages/opencode/src/session/schema.ts (1)
4-31: Good: the session identifier schemas now share the same Zod derivation path.
SessionID,MessageID, andPartIDall stay branded and keep their override-based runtime validators in sync with the Effect definitions.packages/opencode/src/control-plane/schema.ts (1)
4-17: Looks good — the schema bridge stays consistent.The
ZodOverrideannotation pluszod(schema)accessor matches the new Effect-first pattern, and the brandedWorkspaceIDtype is preserved.packages/opencode/src/question/schema.ts (1)
4-15: Looks good — this ID schema now derives from the Effect definition.The
ZodOverrideannotation andzod(this)static keep the runtime and type-level validation in sync.packages/opencode/src/sync/schema.ts (1)
4-11: Looks good — this keepsEventIDsourced from the Effect schema.The override annotation and
zod(s)accessor are consistent with the new schema-generation flow.packages/opencode/src/permission/schema.ts (1)
4-15: Looks good —PermissionIDnow derives from the canonical Effect schema.The new annotation and shared
zod(this)helper keep the validator aligned with the schema source.packages/opencode/script/schema.ts (1)
56-56: Looks good — the generator now consumes the canonical config schema.Using
Config.Info.zodhere keeps the emitted JSON schema in sync with the rest of the refactor.packages/opencode/src/server/instance/config.ts (1)
26-27: Looks good — request/response contracts now share the same schema.Using
Config.Info.zodfor bothresolver(...)andvalidator(...)keeps the documented API and runtime validation aligned.Also applies to: 47-48, 54-54
packages/opencode/test/session/compaction.test.ts (1)
177-180: Looks good — the test fixture now uses the Zod-backed config parser.That keeps the mocked
Config.Servicealigned with the new schema source.packages/opencode/src/config/agent.ts (1)
6-6: Looks good — the permission field now uses the concrete schema.This keeps the agent config on the shared Effect schema path and removes the need for the old override placeholder.
Also applies to: 51-51
packages/opencode/src/server/instance/global.ts (1)
195-196: Consistent.zodmigration for both OpenAPI and runtime validation.Good update: docs (
resolver) and request validator now point to the sameConfig.Info.zodschema source.Also applies to: 216-217, 223-223
packages/opencode/test/config/config.test.ts (1)
2961-2963:parseManagedPlistschema calls are correctly aligned toConfig.Info.zod.Nice consistency update across all parseManagedPlist unit tests.
Also applies to: 2993-2995, 3013-3015, 3043-3045, 3060-3062
packages/opencode/test/permission/next.test.ts (1)
87-147: Great coverage for wildcard-vs-specific precedence invariants.These additions lock in the key behavioral contract (
*fallback, specific override, and preserved intra-key pattern order) very well.packages/opencode/src/config/permission.ts (1)
54-77: Normalization + derived.zodwiring looks solid.The
InputSchema -> decodeTo(InputObject)path cleanly preserves shorthand support while keeping a canonical decoded shape for downstream consumers.packages/opencode/src/config/config.ts (1)
148-156: Canonical schema +.zodcompatibility migration is implemented consistently.Strict derived
Info.zodplus updated parse call sites are aligned and reduce split-schema drift risk.Also applies to: 252-252, 298-305, 435-436, 851-852, 857-858
Outside-diff comment reply (
|
205cdf4 to
c04d805
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/opencode/test/config/config.test.ts (1)
2116-2135: 🛠️ Refactor suggestion | 🟠 MajorUse
tmpdir({ config: ... })for this fixture.This case only needs an
opencode.json, so the manualFilesystem.write(...)setup is extra plumbing and diverges from the repo fixture contract.♻️ Suggested simplification
- await using tmp = await tmpdir({ - init: async (dir) => { - await Filesystem.write( - path.join(dir, "opencode.json"), - JSON.stringify({ - $schema: "https://opencode.ai/config.json", - permission: { - "*": "deny", - edit: "ask", - write: "ask", - external_directory: "ask", - read: "allow", - todowrite: "allow", - "thoughts_*": "allow", - "reasoning_model_*": "allow", - "tools_*": "allow", - "pr_comments_*": "allow", - }, - }), - ) - }, - }) + await using tmp = await tmpdir({ + config: { + permission: { + "*": "deny", + edit: "ask", + write: "ask", + external_directory: "ask", + read: "allow", + todowrite: "allow", + "thoughts_*": "allow", + "reasoning_model_*": "allow", + "tools_*": "allow", + "pr_comments_*": "allow", + }, + }, + })As per coding guidelines, "Use the
configoption intmpdirto write anopencode.jsonconfig file during test setup by passing a partial Config.Info object."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/opencode/test/config/config.test.ts` around lines 2116 - 2135, The test is manually writing opencode.json with Filesystem.write inside the tmpdir init; instead use the tmpdir helper's config option to create the config file. Replace the tmpdir({ init: async (dir) => { Filesystem.write(... "opencode.json" ...) } }) usage with tmpdir({ config: <partial Config.Info object matching the JSON shown> }) so the opencode.json is created by tmpdir; update the test to pass the same permission object via the config parameter and remove the Filesystem.write call and the init function. Ensure you modify the invocation named tmpdir and remove reliance on Filesystem.write.
🤖 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/opencode/src/permission/index.ts`:
- Around line 294-303: The loop that builds ruleset lets a legacy key (e.g.,
rawKey "task") mapped via LEGACY_KEY_ALIASES to the canonical key ("agent")
override an earlier explicit canonical entry; to fix, after resolving key =
LEGACY_KEY_ALIASES[rawKey] ?? rawKey, detect legacy-to-canonical cases (rawKey
!== key) and skip adding the legacy entry when the original permission object
already contains the canonical key (use
Object.prototype.hasOwnProperty.call(permission, key) or similar); update the
for (const [rawKey, value] of entries) block so legacy aliases are ignored when
a canonical property exists, preserving canonical precedence in
ruleset/evaluate()/disabled().
In `@packages/opencode/src/util/effect-zod.ts`:
- Around line 65-72: The current logic uses `override ?? bodyWithChecks(ast)`
which skips applying `ast.checks` when `override` is present; change this so
checks are always layered onto the chosen schema: if `override` exists call the
check-applying helper (the same logic used by `bodyWithChecks`/`applyChecks`)
with `override` and `ast.checks`, otherwise use `bodyWithChecks(ast)`; keep the
subsequent use of `SchemaAST.resolveDescription(ast)` and
`SchemaAST.resolveIdentifier(ast)` and apply `describe` and `meta({ ref })` to
the result so description/ref metadata remains unchanged.
---
Outside diff comments:
In `@packages/opencode/test/config/config.test.ts`:
- Around line 2116-2135: The test is manually writing opencode.json with
Filesystem.write inside the tmpdir init; instead use the tmpdir helper's config
option to create the config file. Replace the tmpdir({ init: async (dir) => {
Filesystem.write(... "opencode.json" ...) } }) usage with tmpdir({ config:
<partial Config.Info object matching the JSON shown> }) so the opencode.json is
created by tmpdir; update the test to pass the same permission object via the
config parameter and remove the Filesystem.write call and the init function.
Ensure you modify the invocation named tmpdir and remove reliance on
Filesystem.write.
🪄 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: 347ea3b9-dba7-48b8-b147-66347b2ddbe1
⛔ Files ignored due to path filters (1)
packages/sdk/js/src/v2/gen/types.gen.tsis excluded by!**/gen/**
📒 Files selected for processing (23)
packages/opencode/script/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/permission/index.tspackages/opencode/src/permission/schema.tspackages/opencode/src/project/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/session/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/tool/edit.tspackages/opencode/src/tool/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/test/config/config.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (2)
packages/opencode/**/*.ts
📄 CodeRabbit inference engine (packages/opencode/AGENTS.md)
packages/opencode/**/*.ts: UseEffect.gen(function* () { ... })for Effect composition
UseEffect.fn("Domain.method")for named/traced effects andEffect.fnUntracedfor internal helpers; these accept pipeable operators as extra arguments to avoid unnecessary outer.pipe()wrappers
UseEffect.callbackfor callback-based APIs
PreferDateTime.nowAsDateovernew Date(yield* Clock.currentTimeMillis)when you need aDatein Effect code
UseSchema.Classfor multi-field data in Effect schemas
Use branded schemas (Schema.brand) for single-value types in Effect
UseSchema.TaggedErrorClassfor typed errors in Effect schemas
UseSchema.Defectinstead ofunknownfor defect-like causes in Effect code
InEffect.gen/Effect.fn, preferyield* new MyError(...)overyield* Effect.fail(new MyError(...))for direct early-failure branches
UsemakeRuntimefromsrc/effect/run-service.tsfor all services; it returns{ runPromise, runFork, runCallback }backed by a sharedmemoMapthat deduplicates layers
UseInstanceStatefromsrc/effect/instance-state.tsfor per-directory or per-project state that needs per-instance cleanup; do work directly in theInstanceState.makeclosure whereScopedCachehandles run-once semantics
UseEffect.addFinalizerorEffect.acquireReleaseinside theInstanceState.makeclosure for cleanup (subscriptions, process teardown, etc.)
UseEffect.forkScopedinside theInstanceState.makeclosure for background stream consumers — the fiber is interrupted when the instance is disposed
PreferFileSystem.FileSysteminstead of rawfs/promisesfor effectful file I/O in Effect services
PreferChildProcessSpawner.ChildProcessSpawnerwithChildProcess.make(...)instead of custom process wrappers in Effect services
PreferHttpClient.HttpClientinstead of rawfetchin Effect services
PreferPath.Path,Config,Clock, andDateTimeservices when those concerns are already inside Effect code
For backgroun...
Files:
packages/opencode/script/schema.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/project/schema.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/edit.tspackages/opencode/src/tool/schema.tspackages/opencode/src/permission/index.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.tspackages/opencode/test/config/config.test.ts
packages/opencode/test/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (packages/opencode/test/AGENTS.md)
packages/opencode/test/**/*.test.{ts,tsx}: Use thetmpdirfunction fromfixture/fixture.tsto create temporary directories for tests with automatic cleanup. Useawait usingsyntax to ensure automatic cleanup when the variable goes out of scope.
When using thetmpdirfunction with git repository support, pass thegit: trueoption to initialize a git repo with a root commit.
Use theconfigoption intmpdirto write anopencode.jsonconfig file during test setup by passing a partial Config.Info object.
Use theinitoption intmpdirto define custom setup functions that can return extra data accessible viatmp.extra, and use thedisposeoption for custom cleanup logic.
UsetestEffect(...)fromtest/lib/effect.tsfor tests that exercise Effect services or Effect-based workflows.
Useit.effect(...)when the test should run withTestClockandTestConsole. Useit.live(...)when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.
Prefer Effect-aware helpers fromfixture/fixture.tsover building manual runtimes in tests: usetmpdirScoped()for scoped temp directories,provideInstance(dir)(effect)for low-level binding without directory creation,provideTmpdirInstance(...)for single temp instance binding, orprovideTmpdirServer(...)for tests that also need the test LLM server.
Defineconst it = testEffect(...)near the top of the test file and keep the test body insideEffect.gen(function* () { ... }). Yield services directly withyield* MyService.Serviceoryield* MyTool.
Avoid customManagedRuntime,attach(...), or ad hocrun(...)wrappers in Effect tests whentestEffect(...)already provides the runtime.
When a test needs instance-local state, preferprovideTmpdirInstance(...)orprovideInstance(...)over manualInstance.provide(...)inside Promise-style tests.
Files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/config/config.test.ts
🧠 Learnings (47)
📓 Common learnings
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch-auth.test.ts:0-0
Timestamp: 2026-04-27T11:19:24.963Z
Learning: In `packages/opencode/test/tool/websearch-auth.test.ts` (Astro-Han/pawwork), the tests intentionally use a small local `runWith` runner with raw `bun:test` and `Effect.runPromise` rather than the `testEffect` harness. Each test case injects a custom in-memory `Auth.Service` layer; switching to `testEffect` would be style-only churn without changing risk coverage. Do not flag these tests as needing harness migration.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:12.228Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/prompt.ts` and `packages/opencode/src/session/processor.ts`, PR `#264`), the loop-gate race condition between `buildLoopContext()` and `recordSyntheticBlock`/`recordSyntheticStop` is intentionally handled via idempotence guards (re-check sigKey presence / `hasStopped` inside the record helpers) rather than a full per-parent `Effect.Mutex`. Threading a `Map<MessageID, Mutex>` through the processor was considered too large a surface change for this edge case; the residual TOCTOU window only produces extra synthetic parts with no behavioral drift on the "turn ends" contract. A code comment documents the trade-off and points to a full-mutex follow-up if the race is observed in practice. Do NOT re-flag the absence of a per-parent mutex as a blocking issue in future reviews.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/mcp-exa.test.ts:1-186
Timestamp: 2026-04-27T11:18:47.332Z
Learning: In `packages/opencode/test/tool/mcp-exa.test.ts` (Astro-Han/pawwork), the tests intentionally use raw `bun:test` async cases with `Effect.runPromise(...)` and per-case `HttpClient.make(...)` fakes rather than the `testEffect(...)` harness. The maintainer has explicitly decided not to migrate, because the HttpClient fake wiring is itself the behavior under test and switching to `testEffect` would be style churn without changing risk coverage. Do not flag these tests as needing harness migration.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/opencode/test/provider/provider.test.ts:64-85
Timestamp: 2026-04-22T09:32:54.556Z
Learning: In `packages/opencode/test/provider/provider.test.ts`, the file intentionally uses AppRuntime-based async helpers (`run`, `list`, `getProvider`, etc.) rather than `testEffect(...)` for all tests. Converting individual tests to `testEffect` while leaving the rest on the async pattern would create internal inconsistency. A full harness migration of this file is the right approach if the pattern needs to change, but that should be a separate PR.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/tool/todo.ts:7-22
Timestamp: 2026-04-28T02:24:27.738Z
Learning: In `packages/opencode/src/tool/todo.ts` (Astro-Han/pawwork, PR `#280`), the `parameters` constant for `TodoWriteTool` intentionally duplicates the `Todo.Info` fields inline rather than referencing `Todo.Info` or `zodObject(Todo.Info)`. After the Effect Schema migration in this PR, `Todo.Info` is an Effect Schema, so `zodObject(Todo.Info)` returns `z.ZodObject<any>` — which erases per-field types that `Tool.define`'s `execute()` inference depends on. The inline Zod schema is a deliberate temporary measure; tool schemas will migrate to Effect Schema as a separate slice per `specs/effect/schema.md`. Do NOT suggest replacing the inline schema with `z.array(Todo.Info)` or `zodObject(Todo.Info)` until that migration lands.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 211
File: packages/opencode/src/provider/models.ts:113-179
Timestamp: 2026-04-24T06:50:05.142Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/provider/models.ts`), `PublishModel` and `PublishProvider` intentionally duplicate fields from `Model` and `Provider` with looser optionality. The split is a deliberate behavior boundary: `PublishModel`/`PublishProvider` (with `.passthrough()`) are used for lenient publish-time validation of incoming catalogs, while `Model`/`Provider` are used for strict runtime normalization. Do not flag this duplication as a maintenance burden or suggest extracting a shared base schema — the explicit separation is intentional and tied to the models-refresh reliability path introduced in PR `#211`.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/session/projectors.ts:0-0
Timestamp: 2026-04-28T01:24:29.645Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/projectors.ts`), `SyncEvent.Definition.schema` preserves the literal Zod type through `z.infer<Def["schema"]>`, so `data.info` inside a `SyncEvent.project(Session.Event.Created, ...)` callback is already typed as `Session.Info` by the TypeScript compiler. No `as Session.Info` cast or `Session.Info.parse()` call is needed at this persistence boundary — a typecheck-clean removal of any such cast is sufficient.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:58.310Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:23.748Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/config/`), `Schema.Struct` and `Schema.Class` are both valid and intentionally used patterns across config modules (e.g. `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`, `command.ts`). Do not flag `Schema.Struct` usage in config schemas as needing conversion to `Schema.Class` — the choice is driven by upstream alignment and local context, not inconsistency.
📚 Learning: 2026-04-28T02:24:27.738Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/tool/todo.ts:7-22
Timestamp: 2026-04-28T02:24:27.738Z
Learning: In `packages/opencode/src/tool/todo.ts` (Astro-Han/pawwork, PR `#280`), the `parameters` constant for `TodoWriteTool` intentionally duplicates the `Todo.Info` fields inline rather than referencing `Todo.Info` or `zodObject(Todo.Info)`. After the Effect Schema migration in this PR, `Todo.Info` is an Effect Schema, so `zodObject(Todo.Info)` returns `z.ZodObject<any>` — which erases per-field types that `Tool.define`'s `execute()` inference depends on. The inline Zod schema is a deliberate temporary measure; tool schemas will migrate to Effect Schema as a separate slice per `specs/effect/schema.md`. Do NOT suggest replacing the inline schema with `z.array(Todo.Info)` or `zodObject(Todo.Info)` until that migration lands.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/project/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-28T01:24:29.645Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/session/projectors.ts:0-0
Timestamp: 2026-04-28T01:24:29.645Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/projectors.ts`), `SyncEvent.Definition.schema` preserves the literal Zod type through `z.infer<Def["schema"]>`, so `data.info` inside a `SyncEvent.project(Session.Event.Created, ...)` callback is already typed as `Session.Info` by the TypeScript compiler. No `as Session.Info` cast or `Session.Info.parse()` call is needed at this persistence boundary — a typecheck-clean removal of any such cast is sufficient.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/project/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-21T16:57:23.748Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:23.748Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/config/`), `Schema.Struct` and `Schema.Class` are both valid and intentionally used patterns across config modules (e.g. `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`, `command.ts`). Do not flag `Schema.Struct` usage in config schemas as needing conversion to `Schema.Class` — the choice is driven by upstream alignment and local context, not inconsistency.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/pty/schema.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use branded schemas (`Schema.brand`) for single-value types in Effect
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/project/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/drizzle.config.ts : Configure Drizzle Kit migrations with schema path `./src/**/*.sql.ts` and output path `./migration`
Applied to files:
packages/opencode/script/schema.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Class` for multi-field data in Effect schemas
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/project/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Defect` instead of `unknown` for defect-like causes in Effect code
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/project/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `config` option in `tmpdir` to write an `opencode.json` config file during test setup by passing a partial Config.Info object.
Applied to files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/config/config.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Prefer Effect-aware helpers from `fixture/fixture.ts` over building manual runtimes in tests: use `tmpdirScoped()` for scoped temp directories, `provideInstance(dir)(effect)` for low-level binding without directory creation, `provideTmpdirInstance(...)` for single temp instance binding, or `provideTmpdirServer(...)` for tests that also need the test LLM server.
Applied to files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-27T11:18:47.596Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/agent/agent.test.ts:440-447
Timestamp: 2026-04-27T11:18:47.596Z
Learning: In `packages/opencode/test/agent/agent.test.ts` (Astro-Han/pawwork), all agent-permission tests intentionally use the manual `tmpdir()` + `Instance.provide(...)` pattern. Do not flag individual tests in this file for conversion to `provideTmpdirInstance(...)` or `provideInstance(...)`; a full harness migration would be a separate PR if the pattern ever needs to change.
Applied to files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-27T11:18:47.332Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/mcp-exa.test.ts:1-186
Timestamp: 2026-04-27T11:18:47.332Z
Learning: In `packages/opencode/test/tool/mcp-exa.test.ts` (Astro-Han/pawwork), the tests intentionally use raw `bun:test` async cases with `Effect.runPromise(...)` and per-case `HttpClient.make(...)` fakes rather than the `testEffect(...)` harness. The maintainer has explicitly decided not to migrate, because the HttpClient fake wiring is itself the behavior under test and switching to `testEffect` would be style churn without changing risk coverage. Do not flag these tests as needing harness migration.
Applied to files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `tmpdir` function from `fixture/fixture.ts` to create temporary directories for tests with automatic cleanup. Use `await using` syntax to ensure automatic cleanup when the variable goes out of scope.
Applied to files:
packages/opencode/test/session/compaction.test.ts
📚 Learning: 2026-04-23T08:51:00.819Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 186
File: packages/opencode/test/plugin/workspace-adaptor.test.ts:139-144
Timestamp: 2026-04-23T08:51:00.819Z
Learning: For pawwork tests under packages/opencode/test/**, auth.json teardown may intentionally combine `Filesystem.write` (from `packages/opencode/src/util/filesystem.ts`) with `node:fs/promises` `unlink` for cleanup. Do not flag this as inconsistent style; it is the established/intentional pattern because `Filesystem` does not provide a `remove`/`unlink` helper.
Applied to files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-27T12:59:45.694Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/prompt-effect.test.ts:0-0
Timestamp: 2026-04-27T12:59:45.694Z
Learning: In session prompt/diagnostics tests (e.g., when verifying injected LLM “recovery reminder” copy), do not assert a single exact reminder phrasing tied to signature kind. If a scenario can produce both `input:` and `target:` signatures (for example, a `read` tool call with a `filePath` parameter), accept either phrasing: the input-repeat variant may say “repeated the same tool input 3 times” (literal count), and the target-repeat variant may say “failed against the same target multiple times” (“multiple times” without a count). Your assertions should allow both variants rather than narrowing to only the input-variant text.
Applied to files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.ts
📚 Learning: 2026-04-26T16:34:57.130Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 247
File: packages/ui/src/components/message-part.tsx:1322-1324
Timestamp: 2026-04-26T16:34:57.130Z
Learning: In Astro-Han/pawwork (`packages/ui/src/components/message-part.tsx`), the `taskId` createMemo and `childSessionId` createMemo both intentionally read only from `partMetadata().sessionId` (populated post-execution), not from `input.task_id` / `input.subagent_session_id`. This has always been the case — the original code never read the input field either. Adding an `input.subagent_session_id` fallback would be a new capability, not a bug fix. Do NOT flag the absence of this fallback as a regression in PR `#247` or future PRs unless there is a concrete case where metadata is not populated.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-22T08:49:47.800Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/desktop-electron/src/main/index-sidecar-source.test.ts:3-11
Timestamp: 2026-04-22T08:49:47.800Z
Learning: In `packages/desktop-electron/src/main/index-sidecar-source.test.ts` (Astro-Han/pawwork), the test intentionally uses `expect(source).toContain` / `expect(source).not.toContain` string matching against the raw `index.ts` source text as a lightweight sidecar contract guard. The maintainer has explicitly chosen not to introduce an AST parser (e.g., `babel/parser` or acorn) for this purpose. Do not flag these string-based assertions as fragile or suggest converting them to AST-based matching.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/permission/next.test.ts
📚 Learning: 2026-04-27T10:33:12.228Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:12.228Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/prompt.ts` and `packages/opencode/src/session/processor.ts`, PR `#264`), the loop-gate race condition between `buildLoopContext()` and `recordSyntheticBlock`/`recordSyntheticStop` is intentionally handled via idempotence guards (re-check sigKey presence / `hasStopped` inside the record helpers) rather than a full per-parent `Effect.Mutex`. Threading a `Map<MessageID, Mutex>` through the processor was considered too large a surface change for this edge case; the residual TOCTOU window only produces extra synthetic parts with no behavioral drift on the "turn ends" contract. A code comment documents the trade-off and points to a full-mutex follow-up if the race is observed in practice. Do NOT re-flag the absence of a per-parent mutex as a blocking issue in future reviews.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-24T06:50:05.142Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 211
File: packages/opencode/src/provider/models.ts:113-179
Timestamp: 2026-04-24T06:50:05.142Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/provider/models.ts`), `PublishModel` and `PublishProvider` intentionally duplicate fields from `Model` and `Provider` with looser optionality. The split is a deliberate behavior boundary: `PublishModel`/`PublishProvider` (with `.passthrough()`) are used for lenient publish-time validation of incoming catalogs, while `Model`/`Provider` are used for strict runtime normalization. Do not flag this duplication as a maintenance burden or suggest extracting a shared base schema — the explicit separation is intentional and tied to the models-refresh reliability path introduced in PR `#211`.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T10:08:57.448Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/diagnostics.test.ts:313-318
Timestamp: 2026-04-27T10:08:57.448Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/diagnostics.ts`, PR `#264`), `SessionDiagnostics.LoopAction` is a TypeScript string literal union type (`"observe" | "block" | "stop"`), NOT a runtime enum. There is no runtime form to introspect via `Object.values()`. The test pattern `const all: SessionDiagnostics.LoopAction[] = ["observe", "block", "stop"]; expect(all).toHaveLength(3)` is intentional and correct — it provides compile-time exhaustiveness via the type annotation. Do NOT suggest replacing this with `Object.values()` introspection or flag the runtime length check as insufficient.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-25T12:52:39.403Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 234
File: packages/opencode/src/session/export.ts:75-88
Timestamp: 2026-04-25T12:52:39.403Z
Learning: In `packages/opencode/src/session/export.ts` (Astro-Han/pawwork), the `extractReasonFromCause` function intentionally uses a lightweight cast-based inspection of the Cause `reasons` array instead of `Cause.failures()` / `Cause.defects()` utilities. This is a deliberate choice: the function is diagnostic-only (used to populate a best-effort reason string for export diagnostics), and the maintainer explicitly prefers not to add coupling to Effect's Cause API surface for this purpose. Do not flag this pattern as fragile or suggest replacing it with Cause utilities.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-27T08:27:43.672Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/diagnostics.ts:255-279
Timestamp: 2026-04-27T08:27:43.672Z
Learning: In `packages/opencode/src/session/diagnostics.ts` (PR `#264`, issue `#229`), target-level loop detection is intentionally tool-scoped. The `real` filter includes `r.tool === input.tool` and target signature keys are formatted as `target:${tool}:${targetHash}`. Cross-tool retries on the same URL/path/query are considered legitimate exploration (e.g., webfetch failed → switch to a different tool), NOT a stuck loop. Only same-tool + same-target accumulation counts toward block/stop. Do NOT suggest removing the tool scope from target signature keys or the `real` filter.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-27T11:19:24.963Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch-auth.test.ts:0-0
Timestamp: 2026-04-27T11:19:24.963Z
Learning: In `packages/opencode/test/tool/websearch-auth.test.ts` (Astro-Han/pawwork), the tests intentionally use a small local `runWith` runner with raw `bun:test` and `Effect.runPromise` rather than the `testEffect` harness. Each test case injects a custom in-memory `Auth.Service` layer; switching to `testEffect` would be style-only churn without changing risk coverage. Do not flag these tests as needing harness migration.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/tool/todo.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T10:33:02.677Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/diagnostics.ts:245-252
Timestamp: 2026-04-27T10:33:02.677Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/diagnostics.ts`, PR `#264`), `loopLastError` is stored as raw (unscrubbed) error text in loop metadata intentionally. The trust boundary for sensitive data scrubbing is the renderer (`LoopRenderer.render` already applies `scrubErrorText` before any user/model-facing output), not the storage layer. Failed tool parts also already store raw errors in `state.error` (PR `#204` default behavior), so scrubbing only `loopLastError` would not close that pre-existing leak and would add lossy storage. If export-side hardening is needed, the correct approach is a single sanitizer pass over both `state.error` and metadata in the export layer. Do NOT suggest scrubbing `loopLastError` at the `observeToolError` call site.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-24T03:51:56.211Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 206
File: packages/app/e2e/prompt/prompt-footer-focus.spec.ts:131-143
Timestamp: 2026-04-24T03:51:56.211Z
Learning: In Astro-Han/pawwork E2E tests (packages/app/e2e/fixtures.ts), `project.prompt(text)` internally calls `trackSession(next.sessionID, active.directory)` after the prompt submission is observed and the active session is resolved. Tests that obtain a `sessionID` from `project.prompt()` do NOT need to call `project.trackSession(sessionID)` manually — it is already registered for teardown. Calling it again would be duplicate ownership.
Applied to files:
packages/opencode/test/session/session.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `testEffect(...)` from `test/lib/effect.ts` for tests that exercise Effect services or Effect-based workflows.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` wrappers in Effect tests when `testEffect(...)` already provides the runtime.
Applied to files:
packages/opencode/test/util/effect-zod.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Define `const it = testEffect(...)` near the top of the test file and keep the test body inside `Effect.gen(function* () { ... })`. Yield services directly with `yield* MyService.Service` or `yield* MyTool`.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-22T09:32:54.556Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/opencode/test/provider/provider.test.ts:64-85
Timestamp: 2026-04-22T09:32:54.556Z
Learning: In `packages/opencode/test/provider/provider.test.ts`, the file intentionally uses AppRuntime-based async helpers (`run`, `list`, `getProvider`, etc.) rather than `testEffect(...)` for all tests. Converting individual tests to `testEffect` while leaving the rest on the async pattern would create internal inconsistency. A full harness migration of this file is the right approach if the pattern needs to change, but that should be a separate PR.
Applied to files:
packages/opencode/test/util/effect-zod.test.ts
📚 Learning: 2026-04-27T11:18:47.298Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch.test.ts:21-78
Timestamp: 2026-04-27T11:18:47.298Z
Learning: In `packages/opencode/test/tool/websearch.test.ts`, the tests intentionally use manual `Effect.runPromise` with explicit `Effect.provide(...)` chains (including `Layer.succeed(Auth.Service, ...)`, `Layer.succeed(HttpClient.HttpClient, http)`, `WebSearchAuth.layer`, `Truncate.defaultLayer`, and `Agent.defaultLayer`) rather than the `testEffect(...)` harness. This is by design: the fake Auth and HTTP recovery-metadata layers must be explicitly injected and kept visible/scoped at the test site. Do NOT suggest migrating these tests to `testEffect` or removing the manual layer provides.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.TaggedErrorClass` for typed errors in Effect schemas
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.gen(function* () { ... })` for Effect composition
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-26T15:35:36.505Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 245
File: packages/opencode/src/question/index.ts:21-24
Timestamp: 2026-04-26T15:35:36.505Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/question/index.ts`), `Question.Option.description` is intentionally **required** (`z.string()`, not `.optional()`). This matches the upstream opencode contract and all current callers (e.g. `PlanExitTool`) always supply a description. The defensive `<Show when={props.description}>` rendering in `session-question-dock.tsx` is a standard guard, not a signal that the field is intended to be optional. Do NOT suggest making `Option.description` optional without a dedicated follow-up that covers schema + tool description + dock copy + tests.
Applied to files:
packages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T12:59:49.844Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/prompt-effect.test.ts:0-0
Timestamp: 2026-04-27T12:59:49.844Z
Learning: In `packages/opencode/test/session/prompt-effect.test.ts` and `packages/opencode/src/session/diagnostics.ts` (PR `#264`), the recovery reminder copy differs between signature kinds: the input-repeat variant says "repeated the same tool input 3 times" (uses a literal count), while the target-repeat variant says "failed against the same target multiple times" (uses "multiple times" with no count). Assertions that check for injected reminder text in LLM inputs must accept both phrasings when a scenario produces both `input:` and `target:` signatures (e.g., `read` tool with a `filePath` parameter). Do NOT narrow the assertion to only the input-variant phrasing.
Applied to files:
packages/opencode/src/tool/todo.ts
📚 Learning: 2026-04-27T08:58:00.665Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:531-538
Timestamp: 2026-04-27T08:58:00.665Z
Learning: When using Effect (e.g., `yield*` with `Effect`-style generator yielding), only use `yield* new SomeErrorClass(...)` if `SomeErrorClass` extends `Schema.TaggedErrorClass` (i.e., it implements Effect’s Yieldable interface). For plain `Error` subclasses (like `BlockedLoopError` / `LoopStopError`) or inline `new Error(...)` values, they are not yieldable and must be wrapped as `yield* Effect.fail(new PlainError(...))`. Do not recommend changing `yield* Effect.fail(new SomePlainError(...))` to `yield* new SomePlainError(...)` unless the error class extends `Schema.TaggedErrorClass`.
Applied to files:
packages/opencode/src/tool/todo.tspackages/opencode/src/project/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/edit.tspackages/opencode/src/tool/schema.tspackages/opencode/src/permission/index.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/sync/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : In `Effect.gen` / `Effect.fn`, prefer `yield* new MyError(...)` over `yield* Effect.fail(new MyError(...))` for direct early-failure branches
Applied to files:
packages/opencode/src/tool/todo.ts
📚 Learning: 2026-04-21T16:57:25.580Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/agent.ts:108-119
Timestamp: 2026-04-21T16:57:25.580Z
Learning: In `packages/opencode/src/config/agent.ts` (Astro-Han/pawwork), `ConfigPermission.Info` only accepts permission objects or the three action strings `"ask"`, `"allow"`, `"deny"`, and transforms those action strings into `{ "*": action }` before `normalize()` runs. By the time `normalize()` is reached, `configuredPermission` is always either `undefined` or a `Record<string, Rule>` — never a raw arbitrary string. The `Object.assign(permission, configuredPermission)` pattern is therefore safe. Do not flag it as corrupting string permission references.
Applied to files:
packages/opencode/test/permission/next.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/permission/index.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Prefer `Path.Path`, `Config`, `Clock`, and `DateTime` services when those concerns are already inside Effect code
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Prefer `FileSystem.FileSystem` instead of raw `fs/promises` for effectful file I/O in Effect services
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.fn("Domain.method")` for named/traced effects and `Effect.fnUntraced` for internal helpers; these accept pipeable operators as extra arguments to avoid unnecessary outer `.pipe()` wrappers
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-21T16:57:20.257Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:20.257Z
Learning: In packages/opencode/src/config/ config modules, treat both `Schema.Struct` and `Schema.Class` as valid schema patterns and do not require converting `Schema.Struct` to `Schema.Class`. If `Schema.Struct` is used in a config schema (e.g., in files like `command.ts`, `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`), accept it as intentional; the selection should be based on upstream alignment and the local schema context, not on a rule that one form must replace the other.
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/config/permission.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-27T10:32:59.274Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/diagnostics.ts:245-252
Timestamp: 2026-04-27T10:32:59.274Z
Learning: In pawwork’s session diagnostics/loop error handling (e.g., packages/opencode/src/session/diagnostics.ts), keep `loopLastError` stored as raw/unscrubbed error text in loop metadata. The intended trust boundary for sensitive-data scrubbing is the renderer: `LoopRenderer.render` must apply `scrubErrorText` before any user/model-facing output. Do not recommend scrubbing `loopLastError` at the `observeToolError` call site, because failed tool parts already store raw errors in `state.error` (pre-existing leak) and scrubbing only `loopLastError` would be lossy without closing the gap. If export-side hardening is required, implement a single sanitizer pass in the export layer over both `state.error` and the loop/metadata fields, rather than sanitizing only `loopLastError` earlier in the storage path.
Applied to files:
packages/opencode/src/session/schema.ts
📚 Learning: 2026-04-27T10:33:08.974Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:08.974Z
Learning: For Astro-Han/pawwork session processing code under packages/opencode/src/session/ (e.g., prompt.ts and processor.ts), this specific loop-gate TOCTOU/race between buildLoopContext() and recordSyntheticBlock/recordSyntheticStop is intentionally handled via idempotence guards (re-check sigKey presence and hasStopped inside the record helpers). Do not treat the absence of a per-parent Effect.Mutex as a blocking review issue for this known edge case; the residual window should only create extra synthetic parts and must not change the "turn ends" contract. Respect the existing code comment documenting the trade-off, and only escalate to a full-mutex approach if the race is observed in practice.
Applied to files:
packages/opencode/src/session/schema.ts
📚 Learning: 2026-04-20T14:21:56.373Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 71
File: packages/app/src/components/session/session-status-connections.tsx:146-147
Timestamp: 2026-04-20T14:21:56.373Z
Learning: In the Astro-Han/pawwork repository (SolidJS app), `sync.data.config` is always initialized to `{}` at `packages/app/src/context/global-sync.tsx` line 71 and is never `undefined` at runtime. Non-optional property access like `sync.data.config.plugin` is intentional and consistent with the pattern used in `packages/app/src/components/status-popover-body.tsx` line 243. Do not flag `sync.data.config.plugin` as needing optional chaining.
Applied to files:
packages/opencode/src/config/permission.ts
📚 Learning: 2026-04-24T17:08:46.780Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 224
File: packages/app/src/i18n/zh.ts:0-0
Timestamp: 2026-04-24T17:08:46.780Z
Learning: In Astro-Han/pawwork PR `#224`, the first-occurrence `PawWork 爪印` branding rule originally specified in issue `#196` was superseded by an updated Chinese-branding spec. On all zh UI surfaces in `packages/app/src/i18n/zh.ts` (e.g., `dialog.model.unpaid.freeModels.title`, `session.new.subtitle`, `sidebar.gettingStarted.line1`), the correct and intentional target is fully localized `爪印` branding — no `PawWork` prefix. Do NOT flag these strings as missing the first-occurrence `PawWork 爪印` rule in future reviews.
Applied to files:
packages/opencode/src/config/permission.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.cached` when multiple concurrent callers should share a single in-flight computation rather than storing `Fiber | undefined` or `Promise | undefined` manually
Applied to files:
packages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `InstanceState` from `src/effect/instance-state.ts` for per-directory or per-project state that needs per-instance cleanup; do work directly in the `InstanceState.make` closure where `ScopedCache` handles run-once semantics
Applied to files:
packages/opencode/src/config/config.ts
c04d805 to
ebd1167
Compare
Outside-diff comment reply (
|
Squashes upstream PRs #23716, #23740, #23744, #23745, #23747, #23749,
#23752, #23754, #23756, #23757, #23763, #24005, #24040, #24169, #24213
into a single PawWork commit. Ranges over 16 upstream commits in
date order from upstream's Effect Schema migration batch.
What landed:
- Config.Info, ConfigPermission.Info, ConfigAgent.Info schema definitions
migrated to Effect Schema canonical form. PawWork callers use
Config.Info.zod accessor where Hono validators or Zod-style decoders
are still needed.
- Schema definition files in control-plane, permission, project, pty,
question, sync, tool, session migrated to Effect Schema.
- packages/opencode/src/util/effect-zod.ts walker enhanced to derive
.zod from Effect Schema at runtime, plus tests.
- packages/sdk/js/v2/gen/types.gen.ts regenerated to reflect schema
shape changes.
What was preserved (PawWork divergence kept on Zod / namespace API):
- packages/opencode/src/session/{session,message-v2,prompt,export,message,revert,summary,todo,status,compaction,projectors}.ts
— PawWork's session domain stays Zod-shaped to keep MessageV2
PawWork-only types (FileSource/SymbolSource/ResourceSource carry
PawWork attachment metadata) compatible with current callers.
- packages/opencode/src/bus/bus-event.ts — PawWork keeps Zod-typed
BusEvent.define so the 30+ PawWork test fixtures and divergent
consumers (server/instance/*, acp/agent.ts, file/watcher.ts) keep
reading evt.properties as their schema's inferred type rather than
unknown.
- packages/opencode/src/{snapshot,permission,mcp,provider}/index.ts —
these modules use a PawWork "namespace + XValue alias" pattern that
upstream redesigned. Keeping HEAD here preserves Snapshot.track,
Permission.ask, Mcp.add, Provider.list etc. for PawWork callers.
- packages/opencode/src/provider/* — PawWork's volcengine/zen
integration carries divergent code paths (VOLCENGINE_PLAN constants,
HTTP-Referer/X-Title carve-out per project_rename_blockers.md).
- packages/opencode/src/{file,project,pty,sync,ide,lsp/client,command,
control-plane/workspace,worktree,question,util/schema}/{index,*}.ts
— kept PawWork API surface for callers.
Skipped commits in this range (no PawWork-applicable change after
carve-outs): 93940a1859 (provider domain) — provider.ts is a PawWork
permanent carve-out; the migration is reapplied to PawWork's local
provider only when divergence settles.
Verified: typecheck clean across packages/{core, opencode, util, ui,
desktop-electron}.
Refs: #209
ebd1167 to
42557db
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
packages/opencode/test/session/session.test.ts (1)
126-129:⚠️ Potential issue | 🟡 MinorNarrow the captured bus payload to
step-finishin the subscriber.At Line [128], the handler stores every
PartUpdatedvariant intoreceived; a non-step-finishevent can overwrite it and make this assertion path flaky.🔧 Suggested fix
- let received: MessageV2.Part | undefined + let received: MessageV2.StepFinishPart | undefined const unsub = Bus.subscribe(MessageV2.Event.PartUpdated, (event) => { - received = event.properties.part as MessageV2.Part + const part = event.properties.part + if (part.type !== "step-finish") return + received = part as MessageV2.StepFinishPart }) @@ - expect(received!.type).toBe("step-finish") - const finish = received as MessageV2.StepFinishPart + const finish = received!Also applies to: 152-155
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/opencode/test/session/session.test.ts` around lines 126 - 129, The subscriber for MessageV2.Event.PartUpdated is currently assigning every variant into the shared received variable, which allows non-`step-finish` parts to overwrite it; update the handler registered with Bus.subscribe(MessageV2.Event.PartUpdated, ...) used in the test (the received variable and unsub closure) to guard on the part being the `step-finish` variant (check the event payload property that denotes variant/kind, e.g., event.properties.part.kind or event.properties.part.event === 'step-finish') and only set received when that guard passes; apply the same narrowing change to the other subscriber instance referenced at lines 152-155.
🤖 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/opencode/src/config/config.ts`:
- Around line 148-155: The exported Config.Info was changed to an Effect schema
which removes direct Zod methods (breaking callers); restore a thin
compatibility shim by keeping the new Effect schema (e.g., ConfigInfo) but
re-exporting a namespace-compatible surface named Config.Info that exposes the
original Zod-style API (at minimum a .zod reference to the Effect-derived Zod
schema and a parse alias), and also re-export Config.parse = Config.Info.parse
if parse was previously part of the public namespace; update the symbol
references around Config.Info / ConfigInfo to return this shim so existing
callers keep working.
---
Duplicate comments:
In `@packages/opencode/test/session/session.test.ts`:
- Around line 126-129: The subscriber for MessageV2.Event.PartUpdated is
currently assigning every variant into the shared received variable, which
allows non-`step-finish` parts to overwrite it; update the handler registered
with Bus.subscribe(MessageV2.Event.PartUpdated, ...) used in the test (the
received variable and unsub closure) to guard on the part being the
`step-finish` variant (check the event payload property that denotes
variant/kind, e.g., event.properties.part.kind or event.properties.part.event
=== 'step-finish') and only set received when that guard passes; apply the same
narrowing change to the other subscriber instance referenced at lines 152-155.
🪄 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: f091ab74-dd17-4536-a452-cade18d46e57
⛔ Files ignored due to path filters (1)
packages/sdk/js/src/v2/gen/types.gen.tsis excluded by!**/gen/**
📒 Files selected for processing (23)
packages/opencode/script/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/permission/index.tspackages/opencode/src/permission/schema.tspackages/opencode/src/project/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/session/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/tool/edit.tspackages/opencode/src/tool/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/test/config/config.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: smoke-macos-arm64
- GitHub Check: unit-windows-opencode-session
- GitHub Check: unit-windows-opencode-server-tools
- GitHub Check: unit-windows-app
- GitHub Check: unit-windows-opencode-config-project
- GitHub Check: unit-windows-desktop
- GitHub Check: unit-app
- GitHub Check: unit-opencode
- GitHub Check: typecheck
- GitHub Check: unit-desktop
- GitHub Check: analyze-js-ts
- GitHub Check: e2e-artifacts
🧰 Additional context used
📓 Path-based instructions (2)
packages/opencode/**/*.ts
📄 CodeRabbit inference engine (packages/opencode/AGENTS.md)
packages/opencode/**/*.ts: UseEffect.gen(function* () { ... })for Effect composition
UseEffect.fn("Domain.method")for named/traced effects andEffect.fnUntracedfor internal helpers; these accept pipeable operators as extra arguments to avoid unnecessary outer.pipe()wrappers
UseEffect.callbackfor callback-based APIs
PreferDateTime.nowAsDateovernew Date(yield* Clock.currentTimeMillis)when you need aDatein Effect code
UseSchema.Classfor multi-field data in Effect schemas
Use branded schemas (Schema.brand) for single-value types in Effect
UseSchema.TaggedErrorClassfor typed errors in Effect schemas
UseSchema.Defectinstead ofunknownfor defect-like causes in Effect code
InEffect.gen/Effect.fn, preferyield* new MyError(...)overyield* Effect.fail(new MyError(...))for direct early-failure branches
UsemakeRuntimefromsrc/effect/run-service.tsfor all services; it returns{ runPromise, runFork, runCallback }backed by a sharedmemoMapthat deduplicates layers
UseInstanceStatefromsrc/effect/instance-state.tsfor per-directory or per-project state that needs per-instance cleanup; do work directly in theInstanceState.makeclosure whereScopedCachehandles run-once semantics
UseEffect.addFinalizerorEffect.acquireReleaseinside theInstanceState.makeclosure for cleanup (subscriptions, process teardown, etc.)
UseEffect.forkScopedinside theInstanceState.makeclosure for background stream consumers — the fiber is interrupted when the instance is disposed
PreferFileSystem.FileSysteminstead of rawfs/promisesfor effectful file I/O in Effect services
PreferChildProcessSpawner.ChildProcessSpawnerwithChildProcess.make(...)instead of custom process wrappers in Effect services
PreferHttpClient.HttpClientinstead of rawfetchin Effect services
PreferPath.Path,Config,Clock, andDateTimeservices when those concerns are already inside Effect code
For backgroun...
Files:
packages/opencode/script/schema.tspackages/opencode/test/session/session.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/permission/index.tspackages/opencode/src/tool/edit.tspackages/opencode/src/config/agent.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/tool/todo.tspackages/opencode/src/tool/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.tspackages/opencode/test/config/config.test.ts
packages/opencode/test/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (packages/opencode/test/AGENTS.md)
packages/opencode/test/**/*.test.{ts,tsx}: Use thetmpdirfunction fromfixture/fixture.tsto create temporary directories for tests with automatic cleanup. Useawait usingsyntax to ensure automatic cleanup when the variable goes out of scope.
When using thetmpdirfunction with git repository support, pass thegit: trueoption to initialize a git repo with a root commit.
Use theconfigoption intmpdirto write anopencode.jsonconfig file during test setup by passing a partial Config.Info object.
Use theinitoption intmpdirto define custom setup functions that can return extra data accessible viatmp.extra, and use thedisposeoption for custom cleanup logic.
UsetestEffect(...)fromtest/lib/effect.tsfor tests that exercise Effect services or Effect-based workflows.
Useit.effect(...)when the test should run withTestClockandTestConsole. Useit.live(...)when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.
Prefer Effect-aware helpers fromfixture/fixture.tsover building manual runtimes in tests: usetmpdirScoped()for scoped temp directories,provideInstance(dir)(effect)for low-level binding without directory creation,provideTmpdirInstance(...)for single temp instance binding, orprovideTmpdirServer(...)for tests that also need the test LLM server.
Defineconst it = testEffect(...)near the top of the test file and keep the test body insideEffect.gen(function* () { ... }). Yield services directly withyield* MyService.Serviceoryield* MyTool.
Avoid customManagedRuntime,attach(...), or ad hocrun(...)wrappers in Effect tests whentestEffect(...)already provides the runtime.
When a test needs instance-local state, preferprovideTmpdirInstance(...)orprovideInstance(...)over manualInstance.provide(...)inside Promise-style tests.
Files:
packages/opencode/test/session/session.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/config/config.test.ts
🧠 Learnings (46)
📓 Common learnings
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:12.228Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/prompt.ts` and `packages/opencode/src/session/processor.ts`, PR `#264`), the loop-gate race condition between `buildLoopContext()` and `recordSyntheticBlock`/`recordSyntheticStop` is intentionally handled via idempotence guards (re-check sigKey presence / `hasStopped` inside the record helpers) rather than a full per-parent `Effect.Mutex`. Threading a `Map<MessageID, Mutex>` through the processor was considered too large a surface change for this edge case; the residual TOCTOU window only produces extra synthetic parts with no behavioral drift on the "turn ends" contract. A code comment documents the trade-off and points to a full-mutex follow-up if the race is observed in practice. Do NOT re-flag the absence of a per-parent mutex as a blocking issue in future reviews.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/tool/todo.ts:7-22
Timestamp: 2026-04-28T02:24:27.738Z
Learning: In `packages/opencode/src/tool/todo.ts` (Astro-Han/pawwork, PR `#280`), the `parameters` constant for `TodoWriteTool` intentionally duplicates the `Todo.Info` fields inline rather than referencing `Todo.Info` or `zodObject(Todo.Info)`. After the Effect Schema migration in this PR, `Todo.Info` is an Effect Schema, so `zodObject(Todo.Info)` returns `z.ZodObject<any>` — which erases per-field types that `Tool.define`'s `execute()` inference depends on. The inline Zod schema is a deliberate temporary measure; tool schemas will migrate to Effect Schema as a separate slice per `specs/effect/schema.md`. Do NOT suggest replacing the inline schema with `z.array(Todo.Info)` or `zodObject(Todo.Info)` until that migration lands.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch-auth.test.ts:0-0
Timestamp: 2026-04-27T11:19:24.963Z
Learning: In `packages/opencode/test/tool/websearch-auth.test.ts` (Astro-Han/pawwork), the tests intentionally use a small local `runWith` runner with raw `bun:test` and `Effect.runPromise` rather than the `testEffect` harness. Each test case injects a custom in-memory `Auth.Service` layer; switching to `testEffect` would be style-only churn without changing risk coverage. Do not flag these tests as needing harness migration.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/opencode/test/provider/provider.test.ts:64-85
Timestamp: 2026-04-22T09:32:54.556Z
Learning: In `packages/opencode/test/provider/provider.test.ts`, the file intentionally uses AppRuntime-based async helpers (`run`, `list`, `getProvider`, etc.) rather than `testEffect(...)` for all tests. Converting individual tests to `testEffect` while leaving the rest on the async pattern would create internal inconsistency. A full harness migration of this file is the right approach if the pattern needs to change, but that should be a separate PR.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/mcp-exa.test.ts:1-186
Timestamp: 2026-04-27T11:18:47.332Z
Learning: In `packages/opencode/test/tool/mcp-exa.test.ts` (Astro-Han/pawwork), the tests intentionally use raw `bun:test` async cases with `Effect.runPromise(...)` and per-case `HttpClient.make(...)` fakes rather than the `testEffect(...)` harness. The maintainer has explicitly decided not to migrate, because the HttpClient fake wiring is itself the behavior under test and switching to `testEffect` would be style churn without changing risk coverage. Do not flag these tests as needing harness migration.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 211
File: packages/opencode/src/provider/models.ts:113-179
Timestamp: 2026-04-24T06:50:05.142Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/provider/models.ts`), `PublishModel` and `PublishProvider` intentionally duplicate fields from `Model` and `Provider` with looser optionality. The split is a deliberate behavior boundary: `PublishModel`/`PublishProvider` (with `.passthrough()`) are used for lenient publish-time validation of incoming catalogs, while `Model`/`Provider` are used for strict runtime normalization. Do not flag this duplication as a maintenance burden or suggest extracting a shared base schema — the explicit separation is intentional and tied to the models-refresh reliability path introduced in PR `#211`.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/session/projectors.ts:0-0
Timestamp: 2026-04-28T01:24:29.645Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/projectors.ts`), `SyncEvent.Definition.schema` preserves the literal Zod type through `z.infer<Def["schema"]>`, so `data.info` inside a `SyncEvent.project(Session.Event.Created, ...)` callback is already typed as `Session.Info` by the TypeScript compiler. No `as Session.Info` cast or `Session.Info.parse()` call is needed at this persistence boundary — a typecheck-clean removal of any such cast is sufficient.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:58.310Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use branded schemas (`Schema.brand`) for single-value types in Effect
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Defect` instead of `unknown` for defect-like causes in Effect code
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:23.748Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/config/`), `Schema.Struct` and `Schema.Class` are both valid and intentionally used patterns across config modules (e.g. `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`, `command.ts`). Do not flag `Schema.Struct` usage in config schemas as needing conversion to `Schema.Class` — the choice is driven by upstream alignment and local context, not inconsistency.
📚 Learning: 2026-04-28T02:24:27.738Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/tool/todo.ts:7-22
Timestamp: 2026-04-28T02:24:27.738Z
Learning: In `packages/opencode/src/tool/todo.ts` (Astro-Han/pawwork, PR `#280`), the `parameters` constant for `TodoWriteTool` intentionally duplicates the `Todo.Info` fields inline rather than referencing `Todo.Info` or `zodObject(Todo.Info)`. After the Effect Schema migration in this PR, `Todo.Info` is an Effect Schema, so `zodObject(Todo.Info)` returns `z.ZodObject<any>` — which erases per-field types that `Tool.define`'s `execute()` inference depends on. The inline Zod schema is a deliberate temporary measure; tool schemas will migrate to Effect Schema as a separate slice per `specs/effect/schema.md`. Do NOT suggest replacing the inline schema with `z.array(Todo.Info)` or `zodObject(Todo.Info)` until that migration lands.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/tool/todo.tspackages/opencode/src/tool/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-28T01:24:29.645Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/session/projectors.ts:0-0
Timestamp: 2026-04-28T01:24:29.645Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/projectors.ts`), `SyncEvent.Definition.schema` preserves the literal Zod type through `z.infer<Def["schema"]>`, so `data.info` inside a `SyncEvent.project(Session.Event.Created, ...)` callback is already typed as `Session.Info` by the TypeScript compiler. No `as Session.Info` cast or `Session.Info.parse()` call is needed at this persistence boundary — a typecheck-clean removal of any such cast is sufficient.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/session/session.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/todo.tspackages/opencode/src/tool/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use branded schemas (`Schema.brand`) for single-value types in Effect
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/todo.tspackages/opencode/src/tool/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-21T16:57:23.748Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:23.748Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/config/`), `Schema.Struct` and `Schema.Class` are both valid and intentionally used patterns across config modules (e.g. `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`, `command.ts`). Do not flag `Schema.Struct` usage in config schemas as needing conversion to `Schema.Class` — the choice is driven by upstream alignment and local context, not inconsistency.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/tool/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/drizzle.config.ts : Configure Drizzle Kit migrations with schema path `./src/**/*.sql.ts` and output path `./migration`
Applied to files:
packages/opencode/script/schema.ts
📚 Learning: 2026-04-26T16:34:57.130Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 247
File: packages/ui/src/components/message-part.tsx:1322-1324
Timestamp: 2026-04-26T16:34:57.130Z
Learning: In Astro-Han/pawwork (`packages/ui/src/components/message-part.tsx`), the `taskId` createMemo and `childSessionId` createMemo both intentionally read only from `partMetadata().sessionId` (populated post-execution), not from `input.task_id` / `input.subagent_session_id`. This has always been the case — the original code never read the input field either. Adding an `input.subagent_session_id` fallback would be a new capability, not a bug fix. Do NOT flag the absence of this fallback as a regression in PR `#247` or future PRs unless there is a concrete case where metadata is not populated.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/permission/index.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T10:33:12.228Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:12.228Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/prompt.ts` and `packages/opencode/src/session/processor.ts`, PR `#264`), the loop-gate race condition between `buildLoopContext()` and `recordSyntheticBlock`/`recordSyntheticStop` is intentionally handled via idempotence guards (re-check sigKey presence / `hasStopped` inside the record helpers) rather than a full per-parent `Effect.Mutex`. Threading a `Map<MessageID, Mutex>` through the processor was considered too large a surface change for this edge case; the residual TOCTOU window only produces extra synthetic parts with no behavioral drift on the "turn ends" contract. A code comment documents the trade-off and points to a full-mutex follow-up if the race is observed in practice. Do NOT re-flag the absence of a per-parent mutex as a blocking issue in future reviews.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/permission/index.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T12:59:45.694Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/prompt-effect.test.ts:0-0
Timestamp: 2026-04-27T12:59:45.694Z
Learning: In session prompt/diagnostics tests (e.g., when verifying injected LLM “recovery reminder” copy), do not assert a single exact reminder phrasing tied to signature kind. If a scenario can produce both `input:` and `target:` signatures (for example, a `read` tool call with a `filePath` parameter), accept either phrasing: the input-repeat variant may say “repeated the same tool input 3 times” (literal count), and the target-repeat variant may say “failed against the same target multiple times” (“multiple times” without a count). Your assertions should allow both variants rather than narrowing to only the input-variant text.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/session/compaction.test.ts
📚 Learning: 2026-04-24T06:50:05.142Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 211
File: packages/opencode/src/provider/models.ts:113-179
Timestamp: 2026-04-24T06:50:05.142Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/provider/models.ts`), `PublishModel` and `PublishProvider` intentionally duplicate fields from `Model` and `Provider` with looser optionality. The split is a deliberate behavior boundary: `PublishModel`/`PublishProvider` (with `.passthrough()`) are used for lenient publish-time validation of incoming catalogs, while `Model`/`Provider` are used for strict runtime normalization. Do not flag this duplication as a maintenance burden or suggest extracting a shared base schema — the explicit separation is intentional and tied to the models-refresh reliability path introduced in PR `#211`.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/session/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T10:08:57.448Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/diagnostics.test.ts:313-318
Timestamp: 2026-04-27T10:08:57.448Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/diagnostics.ts`, PR `#264`), `SessionDiagnostics.LoopAction` is a TypeScript string literal union type (`"observe" | "block" | "stop"`), NOT a runtime enum. There is no runtime form to introspect via `Object.values()`. The test pattern `const all: SessionDiagnostics.LoopAction[] = ["observe", "block", "stop"]; expect(all).toHaveLength(3)` is intentional and correct — it provides compile-time exhaustiveness via the type annotation. Do NOT suggest replacing this with `Object.values()` introspection or flag the runtime length check as insufficient.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-25T12:52:39.403Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 234
File: packages/opencode/src/session/export.ts:75-88
Timestamp: 2026-04-25T12:52:39.403Z
Learning: In `packages/opencode/src/session/export.ts` (Astro-Han/pawwork), the `extractReasonFromCause` function intentionally uses a lightweight cast-based inspection of the Cause `reasons` array instead of `Cause.failures()` / `Cause.defects()` utilities. This is a deliberate choice: the function is diagnostic-only (used to populate a best-effort reason string for export diagnostics), and the maintainer explicitly prefers not to add coupling to Effect's Cause API surface for this purpose. Do not flag this pattern as fragile or suggest replacing it with Cause utilities.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-27T08:27:43.672Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/diagnostics.ts:255-279
Timestamp: 2026-04-27T08:27:43.672Z
Learning: In `packages/opencode/src/session/diagnostics.ts` (PR `#264`, issue `#229`), target-level loop detection is intentionally tool-scoped. The `real` filter includes `r.tool === input.tool` and target signature keys are formatted as `target:${tool}:${targetHash}`. Cross-tool retries on the same URL/path/query are considered legitimate exploration (e.g., webfetch failed → switch to a different tool), NOT a stuck loop. Only same-tool + same-target accumulation counts toward block/stop. Do NOT suggest removing the tool scope from target signature keys or the `real` filter.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-27T11:18:47.332Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/mcp-exa.test.ts:1-186
Timestamp: 2026-04-27T11:18:47.332Z
Learning: In `packages/opencode/test/tool/mcp-exa.test.ts` (Astro-Han/pawwork), the tests intentionally use raw `bun:test` async cases with `Effect.runPromise(...)` and per-case `HttpClient.make(...)` fakes rather than the `testEffect(...)` harness. The maintainer has explicitly decided not to migrate, because the HttpClient fake wiring is itself the behavior under test and switching to `testEffect` would be style churn without changing risk coverage. Do not flag these tests as needing harness migration.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.ts
📚 Learning: 2026-04-27T11:19:24.963Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch-auth.test.ts:0-0
Timestamp: 2026-04-27T11:19:24.963Z
Learning: In `packages/opencode/test/tool/websearch-auth.test.ts` (Astro-Han/pawwork), the tests intentionally use a small local `runWith` runner with raw `bun:test` and `Effect.runPromise` rather than the `testEffect` harness. Each test case injects a custom in-memory `Auth.Service` layer; switching to `testEffect` would be style-only churn without changing risk coverage. Do not flag these tests as needing harness migration.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/tool/todo.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-22T08:49:47.800Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/desktop-electron/src/main/index-sidecar-source.test.ts:3-11
Timestamp: 2026-04-22T08:49:47.800Z
Learning: In `packages/desktop-electron/src/main/index-sidecar-source.test.ts` (Astro-Han/pawwork), the test intentionally uses `expect(source).toContain` / `expect(source).not.toContain` string matching against the raw `index.ts` source text as a lightweight sidecar contract guard. The maintainer has explicitly chosen not to introduce an AST parser (e.g., `babel/parser` or acorn) for this purpose. Do not flag these string-based assertions as fragile or suggest converting them to AST-based matching.
Applied to files:
packages/opencode/test/session/session.test.ts
📚 Learning: 2026-04-23T08:51:00.819Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 186
File: packages/opencode/test/plugin/workspace-adaptor.test.ts:139-144
Timestamp: 2026-04-23T08:51:00.819Z
Learning: For pawwork tests under packages/opencode/test/**, auth.json teardown may intentionally combine `Filesystem.write` (from `packages/opencode/src/util/filesystem.ts`) with `node:fs/promises` `unlink` for cleanup. Do not flag this as inconsistent style; it is the established/intentional pattern because `Filesystem` does not provide a `remove`/`unlink` helper.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-27T10:33:02.677Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/diagnostics.ts:245-252
Timestamp: 2026-04-27T10:33:02.677Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/diagnostics.ts`, PR `#264`), `loopLastError` is stored as raw (unscrubbed) error text in loop metadata intentionally. The trust boundary for sensitive data scrubbing is the renderer (`LoopRenderer.render` already applies `scrubErrorText` before any user/model-facing output), not the storage layer. Failed tool parts also already store raw errors in `state.error` (PR `#204` default behavior), so scrubbing only `loopLastError` would not close that pre-existing leak and would add lossy storage. If export-side hardening is needed, the correct approach is a single sanitizer pass over both `state.error` and metadata in the export layer. Do NOT suggest scrubbing `loopLastError` at the `observeToolError` call site.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T11:18:47.596Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/agent/agent.test.ts:440-447
Timestamp: 2026-04-27T11:18:47.596Z
Learning: In `packages/opencode/test/agent/agent.test.ts` (Astro-Han/pawwork), all agent-permission tests intentionally use the manual `tmpdir()` + `Instance.provide(...)` pattern. Do not flag individual tests in this file for conversion to `provideTmpdirInstance(...)` or `provideInstance(...)`; a full harness migration would be a separate PR if the pattern ever needs to change.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/permission/index.tspackages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/permission.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-24T03:51:56.211Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 206
File: packages/app/e2e/prompt/prompt-footer-focus.spec.ts:131-143
Timestamp: 2026-04-24T03:51:56.211Z
Learning: In Astro-Han/pawwork E2E tests (packages/app/e2e/fixtures.ts), `project.prompt(text)` internally calls `trackSession(next.sessionID, active.directory)` after the prompt submission is observed and the active session is resolved. Tests that obtain a `sessionID` from `project.prompt()` do NOT need to call `project.trackSession(sessionID)` manually — it is already registered for teardown. Calling it again would be duplicate ownership.
Applied to files:
packages/opencode/test/session/session.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `config` option in `tmpdir` to write an `opencode.json` config file during test setup by passing a partial Config.Info object.
Applied to files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/config.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Prefer Effect-aware helpers from `fixture/fixture.ts` over building manual runtimes in tests: use `tmpdirScoped()` for scoped temp directories, `provideInstance(dir)(effect)` for low-level binding without directory creation, `provideTmpdirInstance(...)` for single temp instance binding, or `provideTmpdirServer(...)` for tests that also need the test LLM server.
Applied to files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `testEffect(...)` from `test/lib/effect.ts` for tests that exercise Effect services or Effect-based workflows.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Define `const it = testEffect(...)` near the top of the test file and keep the test body inside `Effect.gen(function* () { ... })`. Yield services directly with `yield* MyService.Service` or `yield* MyTool`.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` wrappers in Effect tests when `testEffect(...)` already provides the runtime.
Applied to files:
packages/opencode/test/util/effect-zod.test.ts
📚 Learning: 2026-04-22T09:32:54.556Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/opencode/test/provider/provider.test.ts:64-85
Timestamp: 2026-04-22T09:32:54.556Z
Learning: In `packages/opencode/test/provider/provider.test.ts`, the file intentionally uses AppRuntime-based async helpers (`run`, `list`, `getProvider`, etc.) rather than `testEffect(...)` for all tests. Converting individual tests to `testEffect` while leaving the rest on the async pattern would create internal inconsistency. A full harness migration of this file is the right approach if the pattern needs to change, but that should be a separate PR.
Applied to files:
packages/opencode/test/util/effect-zod.test.ts
📚 Learning: 2026-04-27T11:18:47.298Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch.test.ts:21-78
Timestamp: 2026-04-27T11:18:47.298Z
Learning: In `packages/opencode/test/tool/websearch.test.ts`, the tests intentionally use manual `Effect.runPromise` with explicit `Effect.provide(...)` chains (including `Layer.succeed(Auth.Service, ...)`, `Layer.succeed(HttpClient.HttpClient, http)`, `WebSearchAuth.layer`, `Truncate.defaultLayer`, and `Agent.defaultLayer`) rather than the `testEffect(...)` harness. This is by design: the fake Auth and HTTP recovery-metadata layers must be explicitly injected and kept visible/scoped at the test site. Do NOT suggest migrating these tests to `testEffect` or removing the manual layer provides.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Defect` instead of `unknown` for defect-like causes in Effect code
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/todo.tspackages/opencode/src/tool/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Class` for multi-field data in Effect schemas
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/todo.tspackages/opencode/src/tool/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.TaggedErrorClass` for typed errors in Effect schemas
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/tool/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.gen(function* () { ... })` for Effect composition
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-27T08:58:00.665Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:531-538
Timestamp: 2026-04-27T08:58:00.665Z
Learning: When using Effect (e.g., `yield*` with `Effect`-style generator yielding), only use `yield* new SomeErrorClass(...)` if `SomeErrorClass` extends `Schema.TaggedErrorClass` (i.e., it implements Effect’s Yieldable interface). For plain `Error` subclasses (like `BlockedLoopError` / `LoopStopError`) or inline `new Error(...)` values, they are not yieldable and must be wrapped as `yield* Effect.fail(new PlainError(...))`. Do not recommend changing `yield* Effect.fail(new SomePlainError(...))` to `yield* new SomePlainError(...)` unless the error class extends `Schema.TaggedErrorClass`.
Applied to files:
packages/opencode/src/project/schema.tspackages/opencode/src/permission/index.tspackages/opencode/src/tool/edit.tspackages/opencode/src/config/agent.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/tool/todo.tspackages/opencode/src/tool/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/question/schema.tspackages/opencode/src/permission/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-21T16:57:25.580Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/agent.ts:108-119
Timestamp: 2026-04-21T16:57:25.580Z
Learning: In `packages/opencode/src/config/agent.ts` (Astro-Han/pawwork), `ConfigPermission.Info` only accepts permission objects or the three action strings `"ask"`, `"allow"`, `"deny"`, and transforms those action strings into `{ "*": action }` before `normalize()` runs. By the time `normalize()` is reached, `configuredPermission` is always either `undefined` or a `Record<string, Rule>` — never a raw arbitrary string. The `Object.assign(permission, configuredPermission)` pattern is therefore safe. Do not flag it as corrupting string permission references.
Applied to files:
packages/opencode/src/permission/index.tspackages/opencode/src/config/agent.tspackages/opencode/src/permission/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-21T16:57:20.257Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:20.257Z
Learning: In packages/opencode/src/config/ config modules, treat both `Schema.Struct` and `Schema.Class` as valid schema patterns and do not require converting `Schema.Struct` to `Schema.Class`. If `Schema.Struct` is used in a config schema (e.g., in files like `command.ts`, `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`), accept it as intentional; the selection should be based on upstream alignment and the local schema context, not on a rule that one form must replace the other.
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Prefer `Path.Path`, `Config`, `Clock`, and `DateTime` services when those concerns are already inside Effect code
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/sync/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Prefer `FileSystem.FileSystem` instead of raw `fs/promises` for effectful file I/O in Effect services
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.fn("Domain.method")` for named/traced effects and `Effect.fnUntraced` for internal helpers; these accept pipeable operators as extra arguments to avoid unnecessary outer `.pipe()` wrappers
Applied to files:
packages/opencode/src/config/agent.ts
📚 Learning: 2026-04-26T15:35:36.505Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 245
File: packages/opencode/src/question/index.ts:21-24
Timestamp: 2026-04-26T15:35:36.505Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/question/index.ts`), `Question.Option.description` is intentionally **required** (`z.string()`, not `.optional()`). This matches the upstream opencode contract and all current callers (e.g. `PlanExitTool`) always supply a description. The defensive `<Show when={props.description}>` rendering in `session-question-dock.tsx` is a standard guard, not a signal that the field is intended to be optional. Do NOT suggest making `Option.description` optional without a dedicated follow-up that covers schema + tool description + dock copy + tests.
Applied to files:
packages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T12:59:49.844Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/prompt-effect.test.ts:0-0
Timestamp: 2026-04-27T12:59:49.844Z
Learning: In `packages/opencode/test/session/prompt-effect.test.ts` and `packages/opencode/src/session/diagnostics.ts` (PR `#264`), the recovery reminder copy differs between signature kinds: the input-repeat variant says "repeated the same tool input 3 times" (uses a literal count), while the target-repeat variant says "failed against the same target multiple times" (uses "multiple times" with no count). Assertions that check for injected reminder text in LLM inputs must accept both phrasings when a scenario produces both `input:` and `target:` signatures (e.g., `read` tool with a `filePath` parameter). Do NOT narrow the assertion to only the input-variant phrasing.
Applied to files:
packages/opencode/src/tool/todo.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : In `Effect.gen` / `Effect.fn`, prefer `yield* new MyError(...)` over `yield* Effect.fail(new MyError(...))` for direct early-failure branches
Applied to files:
packages/opencode/src/tool/todo.ts
📚 Learning: 2026-04-27T10:32:59.274Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/diagnostics.ts:245-252
Timestamp: 2026-04-27T10:32:59.274Z
Learning: In pawwork’s session diagnostics/loop error handling (e.g., packages/opencode/src/session/diagnostics.ts), keep `loopLastError` stored as raw/unscrubbed error text in loop metadata. The intended trust boundary for sensitive-data scrubbing is the renderer: `LoopRenderer.render` must apply `scrubErrorText` before any user/model-facing output. Do not recommend scrubbing `loopLastError` at the `observeToolError` call site, because failed tool parts already store raw errors in `state.error` (pre-existing leak) and scrubbing only `loopLastError` would be lossy without closing the gap. If export-side hardening is required, implement a single sanitizer pass in the export layer over both `state.error` and the loop/metadata fields, rather than sanitizing only `loopLastError` earlier in the storage path.
Applied to files:
packages/opencode/src/session/schema.ts
📚 Learning: 2026-04-27T10:33:08.974Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:08.974Z
Learning: For Astro-Han/pawwork session processing code under packages/opencode/src/session/ (e.g., prompt.ts and processor.ts), this specific loop-gate TOCTOU/race between buildLoopContext() and recordSyntheticBlock/recordSyntheticStop is intentionally handled via idempotence guards (re-check sigKey presence and hasStopped inside the record helpers). Do not treat the absence of a per-parent Effect.Mutex as a blocking review issue for this known edge case; the residual window should only create extra synthetic parts and must not change the "turn ends" contract. Respect the existing code comment documenting the trade-off, and only escalate to a full-mutex approach if the race is observed in practice.
Applied to files:
packages/opencode/src/session/schema.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.cached` when multiple concurrent callers should share a single in-flight computation rather than storing `Fiber | undefined` or `Promise | undefined` manually
Applied to files:
packages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `InstanceState` from `src/effect/instance-state.ts` for per-directory or per-project state that needs per-instance cleanup; do work directly in the `InstanceState.make` closure where `ScopedCache` handles run-once semantics
Applied to files:
packages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:21:56.373Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 71
File: packages/app/src/components/session/session-status-connections.tsx:146-147
Timestamp: 2026-04-20T14:21:56.373Z
Learning: In the Astro-Han/pawwork repository (SolidJS app), `sync.data.config` is always initialized to `{}` at `packages/app/src/context/global-sync.tsx` line 71 and is never `undefined` at runtime. Non-optional property access like `sync.data.config.plugin` is intentional and consistent with the pattern used in `packages/app/src/components/status-popover-body.tsx` line 243. Do not flag `sync.data.config.plugin` as needing optional chaining.
Applied to files:
packages/opencode/src/config/permission.ts
📚 Learning: 2026-04-24T17:08:46.780Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 224
File: packages/app/src/i18n/zh.ts:0-0
Timestamp: 2026-04-24T17:08:46.780Z
Learning: In Astro-Han/pawwork PR `#224`, the first-occurrence `PawWork 爪印` branding rule originally specified in issue `#196` was superseded by an updated Chinese-branding spec. On all zh UI surfaces in `packages/app/src/i18n/zh.ts` (e.g., `dialog.model.unpaid.freeModels.title`, `session.new.subtitle`, `sidebar.gettingStarted.line1`), the correct and intentional target is fully localized `爪印` branding — no `PawWork` prefix. Do NOT flag these strings as missing the first-occurrence `PawWork 爪印` rule in future reviews.
Applied to files:
packages/opencode/src/config/permission.ts
There was a problem hiding this comment.
♻️ Duplicate comments (1)
packages/opencode/src/config/config.ts (1)
148-155:⚠️ Potential issue | 🟠 MajorRestore the direct parse surface on
Config.Info.
Infonow exposes only.zod, which drops the previousConfig.Info.parse(...)/Config.parse(...)API from the exported namespace. If this migration is meant to stay namespace-compatible, bindparse/safeParseoff the derived Zod object here instead of forcing downstream renames.♻️ Compatibility shim
-import { zod, ZodOverride } from "@/util/effect-zod" +import { zodObject, ZodOverride } from "@/util/effect-zod" ... .annotate({ identifier: "Config" }) .pipe( - withStatics((s) => ({ - zod: (zod(s) as unknown as z.ZodObject<any>) - .strict() - .meta({ ref: "Config" }) as unknown as z.ZodType<DeepMutable<Schema.Schema.Type<typeof s>>>, - })), + withStatics((s) => { + const schema = zodObject(s).strict().meta({ ref: "Config" }) + return { + zod: schema as unknown as z.ZodType<DeepMutable<Schema.Schema.Type<typeof s>>>, + parse: schema.parse.bind(schema), + safeParse: schema.safeParse.bind(schema), + } + }), )export namespace Config { export const Info = ConfigInfo + export const parse = ConfigInfo.parseAlso applies to: 299-304
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/opencode/src/config/config.ts` around lines 148 - 155, The exported Config.Info currently exposes only .zod and lost the previous Config.Info.parse/safeParse (and Config.parse) namespace API; restore compatibility by binding parse and safeParse from the derived Zod object to the Config.Info surface (e.g., set Config.Info.parse = Config.Info.zod.parse and Config.Info.safeParse = Config.Info.zod.safeParse or similarly attach parse/safeParse to the exported Config object) so callers can continue using Config.Info.parse(...) and Config.parse(...); ensure the same shim is applied in the other mentioned block (lines referenced 299-304) to keep namespace compatibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@packages/opencode/src/config/config.ts`:
- Around line 148-155: The exported Config.Info currently exposes only .zod and
lost the previous Config.Info.parse/safeParse (and Config.parse) namespace API;
restore compatibility by binding parse and safeParse from the derived Zod object
to the Config.Info surface (e.g., set Config.Info.parse = Config.Info.zod.parse
and Config.Info.safeParse = Config.Info.zod.safeParse or similarly attach
parse/safeParse to the exported Config object) so callers can continue using
Config.Info.parse(...) and Config.parse(...); ensure the same shim is applied in
the other mentioned block (lines referenced 299-304) to keep namespace
compatibility.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: dd0806c0-7f32-46fc-a49c-5894a454d317
⛔ Files ignored due to path filters (1)
packages/sdk/js/src/v2/gen/types.gen.tsis excluded by!**/gen/**
📒 Files selected for processing (24)
packages/opencode/script/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/permission/index.tspackages/opencode/src/permission/schema.tspackages/opencode/src/project/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/session/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/tool/edit.tspackages/opencode/src/tool/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/test/config/config.test.tspackages/opencode/test/permission-agent.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: unit-windows-opencode-config-project
- GitHub Check: unit-windows-opencode-server-tools
- GitHub Check: unit-windows-opencode-session
- GitHub Check: unit-windows-desktop
- GitHub Check: unit-windows-app
- GitHub Check: unit-desktop
- GitHub Check: unit-opencode
- GitHub Check: smoke-macos-arm64
- GitHub Check: e2e-artifacts
- GitHub Check: analyze-js-ts
🧰 Additional context used
📓 Path-based instructions (2)
packages/opencode/**/*.ts
📄 CodeRabbit inference engine (packages/opencode/AGENTS.md)
packages/opencode/**/*.ts: UseEffect.gen(function* () { ... })for Effect composition
UseEffect.fn("Domain.method")for named/traced effects andEffect.fnUntracedfor internal helpers; these accept pipeable operators as extra arguments to avoid unnecessary outer.pipe()wrappers
UseEffect.callbackfor callback-based APIs
PreferDateTime.nowAsDateovernew Date(yield* Clock.currentTimeMillis)when you need aDatein Effect code
UseSchema.Classfor multi-field data in Effect schemas
Use branded schemas (Schema.brand) for single-value types in Effect
UseSchema.TaggedErrorClassfor typed errors in Effect schemas
UseSchema.Defectinstead ofunknownfor defect-like causes in Effect code
InEffect.gen/Effect.fn, preferyield* new MyError(...)overyield* Effect.fail(new MyError(...))for direct early-failure branches
UsemakeRuntimefromsrc/effect/run-service.tsfor all services; it returns{ runPromise, runFork, runCallback }backed by a sharedmemoMapthat deduplicates layers
UseInstanceStatefromsrc/effect/instance-state.tsfor per-directory or per-project state that needs per-instance cleanup; do work directly in theInstanceState.makeclosure whereScopedCachehandles run-once semantics
UseEffect.addFinalizerorEffect.acquireReleaseinside theInstanceState.makeclosure for cleanup (subscriptions, process teardown, etc.)
UseEffect.forkScopedinside theInstanceState.makeclosure for background stream consumers — the fiber is interrupted when the instance is disposed
PreferFileSystem.FileSysteminstead of rawfs/promisesfor effectful file I/O in Effect services
PreferChildProcessSpawner.ChildProcessSpawnerwithChildProcess.make(...)instead of custom process wrappers in Effect services
PreferHttpClient.HttpClientinstead of rawfetchin Effect services
PreferPath.Path,Config,Clock, andDateTimeservices when those concerns are already inside Effect code
For backgroun...
Files:
packages/opencode/script/schema.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/permission-agent.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/tool/edit.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/permission/index.tspackages/opencode/src/question/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/permission/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/control-plane/schema.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/test/config/config.test.tspackages/opencode/src/config/permission.ts
packages/opencode/test/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (packages/opencode/test/AGENTS.md)
packages/opencode/test/**/*.test.{ts,tsx}: Use thetmpdirfunction fromfixture/fixture.tsto create temporary directories for tests with automatic cleanup. Useawait usingsyntax to ensure automatic cleanup when the variable goes out of scope.
When using thetmpdirfunction with git repository support, pass thegit: trueoption to initialize a git repo with a root commit.
Use theconfigoption intmpdirto write anopencode.jsonconfig file during test setup by passing a partial Config.Info object.
Use theinitoption intmpdirto define custom setup functions that can return extra data accessible viatmp.extra, and use thedisposeoption for custom cleanup logic.
UsetestEffect(...)fromtest/lib/effect.tsfor tests that exercise Effect services or Effect-based workflows.
Useit.effect(...)when the test should run withTestClockandTestConsole. Useit.live(...)when the test depends on real time, filesystem mtimes, child processes, git, locks, or other live OS behavior.
Prefer Effect-aware helpers fromfixture/fixture.tsover building manual runtimes in tests: usetmpdirScoped()for scoped temp directories,provideInstance(dir)(effect)for low-level binding without directory creation,provideTmpdirInstance(...)for single temp instance binding, orprovideTmpdirServer(...)for tests that also need the test LLM server.
Defineconst it = testEffect(...)near the top of the test file and keep the test body insideEffect.gen(function* () { ... }). Yield services directly withyield* MyService.Serviceoryield* MyTool.
Avoid customManagedRuntime,attach(...), or ad hocrun(...)wrappers in Effect tests whentestEffect(...)already provides the runtime.
When a test needs instance-local state, preferprovideTmpdirInstance(...)orprovideInstance(...)over manualInstance.provide(...)inside Promise-style tests.
Files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/permission-agent.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/config/config.test.ts
🧠 Learnings (46)
📓 Common learnings
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/tool/todo.ts:7-22
Timestamp: 2026-04-28T02:24:27.738Z
Learning: In `packages/opencode/src/tool/todo.ts` (Astro-Han/pawwork, PR `#280`), the `parameters` constant for `TodoWriteTool` intentionally duplicates the `Todo.Info` fields inline rather than referencing `Todo.Info` or `zodObject(Todo.Info)`. After the Effect Schema migration in this PR, `Todo.Info` is an Effect Schema, so `zodObject(Todo.Info)` returns `z.ZodObject<any>` — which erases per-field types that `Tool.define`'s `execute()` inference depends on. The inline Zod schema is a deliberate temporary measure; tool schemas will migrate to Effect Schema as a separate slice per `specs/effect/schema.md`. Do NOT suggest replacing the inline schema with `z.array(Todo.Info)` or `zodObject(Todo.Info)` until that migration lands.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch-auth.test.ts:0-0
Timestamp: 2026-04-27T11:19:24.963Z
Learning: In `packages/opencode/test/tool/websearch-auth.test.ts` (Astro-Han/pawwork), the tests intentionally use a small local `runWith` runner with raw `bun:test` and `Effect.runPromise` rather than the `testEffect` harness. Each test case injects a custom in-memory `Auth.Service` layer; switching to `testEffect` would be style-only churn without changing risk coverage. Do not flag these tests as needing harness migration.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/opencode/test/provider/provider.test.ts:64-85
Timestamp: 2026-04-22T09:32:54.556Z
Learning: In `packages/opencode/test/provider/provider.test.ts`, the file intentionally uses AppRuntime-based async helpers (`run`, `list`, `getProvider`, etc.) rather than `testEffect(...)` for all tests. Converting individual tests to `testEffect` while leaving the rest on the async pattern would create internal inconsistency. A full harness migration of this file is the right approach if the pattern needs to change, but that should be a separate PR.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:12.228Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/prompt.ts` and `packages/opencode/src/session/processor.ts`, PR `#264`), the loop-gate race condition between `buildLoopContext()` and `recordSyntheticBlock`/`recordSyntheticStop` is intentionally handled via idempotence guards (re-check sigKey presence / `hasStopped` inside the record helpers) rather than a full per-parent `Effect.Mutex`. Threading a `Map<MessageID, Mutex>` through the processor was considered too large a surface change for this edge case; the residual TOCTOU window only produces extra synthetic parts with no behavioral drift on the "turn ends" contract. A code comment documents the trade-off and points to a full-mutex follow-up if the race is observed in practice. Do NOT re-flag the absence of a per-parent mutex as a blocking issue in future reviews.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/mcp-exa.test.ts:1-186
Timestamp: 2026-04-27T11:18:47.332Z
Learning: In `packages/opencode/test/tool/mcp-exa.test.ts` (Astro-Han/pawwork), the tests intentionally use raw `bun:test` async cases with `Effect.runPromise(...)` and per-case `HttpClient.make(...)` fakes rather than the `testEffect(...)` harness. The maintainer has explicitly decided not to migrate, because the HttpClient fake wiring is itself the behavior under test and switching to `testEffect` would be style churn without changing risk coverage. Do not flag these tests as needing harness migration.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 211
File: packages/opencode/src/provider/models.ts:113-179
Timestamp: 2026-04-24T06:50:05.142Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/provider/models.ts`), `PublishModel` and `PublishProvider` intentionally duplicate fields from `Model` and `Provider` with looser optionality. The split is a deliberate behavior boundary: `PublishModel`/`PublishProvider` (with `.passthrough()`) are used for lenient publish-time validation of incoming catalogs, while `Model`/`Provider` are used for strict runtime normalization. Do not flag this duplication as a maintenance burden or suggest extracting a shared base schema — the explicit separation is intentional and tied to the models-refresh reliability path introduced in PR `#211`.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/ui/src/theme/context.tsx:11-16
Timestamp: 2026-04-22T09:32:58.310Z
Learning: In Astro-Han/pawwork (`packages/ui/src/theme/context.tsx` and related files), the renaming of localStorage theme keys from `opencode-*` to `pawwork-*` (THEME_ID, COLOR_SCHEME, THEME_CSS_LIGHT, THEME_CSS_DARK) is intentional and should NOT include a migration path from the old keys. Migrating would re-couple PawWork and OpenCode browser storage namespaces, which the PR is explicitly designed to avoid. A reset to the PawWork default theme on upgrade is acceptable by design.
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:23.748Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/config/`), `Schema.Struct` and `Schema.Class` are both valid and intentionally used patterns across config modules (e.g. `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`, `command.ts`). Do not flag `Schema.Struct` usage in config schemas as needing conversion to `Schema.Class` — the choice is driven by upstream alignment and local context, not inconsistency.
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use branded schemas (`Schema.brand`) for single-value types in Effect
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/session/projectors.ts:0-0
Timestamp: 2026-04-28T01:24:29.645Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/projectors.ts`), `SyncEvent.Definition.schema` preserves the literal Zod type through `z.infer<Def["schema"]>`, so `data.info` inside a `SyncEvent.project(Session.Event.Created, ...)` callback is already typed as `Session.Info` by the TypeScript compiler. No `as Session.Info` cast or `Session.Info.parse()` call is needed at this persistence boundary — a typecheck-clean removal of any such cast is sufficient.
📚 Learning: 2026-04-28T02:24:27.738Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/tool/todo.ts:7-22
Timestamp: 2026-04-28T02:24:27.738Z
Learning: In `packages/opencode/src/tool/todo.ts` (Astro-Han/pawwork, PR `#280`), the `parameters` constant for `TodoWriteTool` intentionally duplicates the `Todo.Info` fields inline rather than referencing `Todo.Info` or `zodObject(Todo.Info)`. After the Effect Schema migration in this PR, `Todo.Info` is an Effect Schema, so `zodObject(Todo.Info)` returns `z.ZodObject<any>` — which erases per-field types that `Tool.define`'s `execute()` inference depends on. The inline Zod schema is a deliberate temporary measure; tool schemas will migrate to Effect Schema as a separate slice per `specs/effect/schema.md`. Do NOT suggest replacing the inline schema with `z.array(Todo.Info)` or `zodObject(Todo.Info)` until that migration lands.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/permission/index.tspackages/opencode/src/question/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/permission/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-28T01:24:29.645Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 280
File: packages/opencode/src/session/projectors.ts:0-0
Timestamp: 2026-04-28T01:24:29.645Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/projectors.ts`), `SyncEvent.Definition.schema` preserves the literal Zod type through `z.infer<Def["schema"]>`, so `data.info` inside a `SyncEvent.project(Session.Event.Created, ...)` callback is already typed as `Session.Info` by the TypeScript compiler. No `as Session.Info` cast or `Session.Info.parse()` call is needed at this persistence boundary — a typecheck-clean removal of any such cast is sufficient.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/permission/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use branded schemas (`Schema.brand`) for single-value types in Effect
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/permission/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-21T16:57:23.748Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:23.748Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/config/`), `Schema.Struct` and `Schema.Class` are both valid and intentionally used patterns across config modules (e.g. `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`, `command.ts`). Do not flag `Schema.Struct` usage in config schemas as needing conversion to `Schema.Class` — the choice is driven by upstream alignment and local context, not inconsistency.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use the `config` option in `tmpdir` to write an `opencode.json` config file during test setup by passing a partial Config.Info object.
Applied to files:
packages/opencode/script/schema.tspackages/opencode/test/session/compaction.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/config.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/drizzle.config.ts : Configure Drizzle Kit migrations with schema path `./src/**/*.sql.ts` and output path `./migration`
Applied to files:
packages/opencode/script/schema.ts
📚 Learning: 2026-04-23T08:51:00.819Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 186
File: packages/opencode/test/plugin/workspace-adaptor.test.ts:139-144
Timestamp: 2026-04-23T08:51:00.819Z
Learning: For pawwork tests under packages/opencode/test/**, auth.json teardown may intentionally combine `Filesystem.write` (from `packages/opencode/src/util/filesystem.ts`) with `node:fs/promises` `unlink` for cleanup. Do not flag this as inconsistent style; it is the established/intentional pattern because `Filesystem` does not provide a `remove`/`unlink` helper.
Applied to files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.tspackages/opencode/test/permission-agent.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/test/config/config.test.ts
📚 Learning: 2026-04-27T12:59:45.694Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/prompt-effect.test.ts:0-0
Timestamp: 2026-04-27T12:59:45.694Z
Learning: In session prompt/diagnostics tests (e.g., when verifying injected LLM “recovery reminder” copy), do not assert a single exact reminder phrasing tied to signature kind. If a scenario can produce both `input:` and `target:` signatures (for example, a `read` tool call with a `filePath` parameter), accept either phrasing: the input-repeat variant may say “repeated the same tool input 3 times” (literal count), and the target-repeat variant may say “failed against the same target multiple times” (“multiple times” without a count). Your assertions should allow both variants rather than narrowing to only the input-variant text.
Applied to files:
packages/opencode/test/session/compaction.test.tspackages/opencode/test/session/session.test.ts
📚 Learning: 2026-04-26T16:34:57.130Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 247
File: packages/ui/src/components/message-part.tsx:1322-1324
Timestamp: 2026-04-26T16:34:57.130Z
Learning: In Astro-Han/pawwork (`packages/ui/src/components/message-part.tsx`), the `taskId` createMemo and `childSessionId` createMemo both intentionally read only from `partMetadata().sessionId` (populated post-execution), not from `input.task_id` / `input.subagent_session_id`. This has always been the case — the original code never read the input field either. Adding an `input.subagent_session_id` fallback would be a new capability, not a bug fix. Do NOT flag the absence of this fallback as a regression in PR `#247` or future PRs unless there is a concrete case where metadata is not populated.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/permission/index.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T10:33:12.228Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:12.228Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/prompt.ts` and `packages/opencode/src/session/processor.ts`, PR `#264`), the loop-gate race condition between `buildLoopContext()` and `recordSyntheticBlock`/`recordSyntheticStop` is intentionally handled via idempotence guards (re-check sigKey presence / `hasStopped` inside the record helpers) rather than a full per-parent `Effect.Mutex`. Threading a `Map<MessageID, Mutex>` through the processor was considered too large a surface change for this edge case; the residual TOCTOU window only produces extra synthetic parts with no behavioral drift on the "turn ends" contract. A code comment documents the trade-off and points to a full-mutex follow-up if the race is observed in practice. Do NOT re-flag the absence of a per-parent mutex as a blocking issue in future reviews.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/permission/index.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-24T06:50:05.142Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 211
File: packages/opencode/src/provider/models.ts:113-179
Timestamp: 2026-04-24T06:50:05.142Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/provider/models.ts`), `PublishModel` and `PublishProvider` intentionally duplicate fields from `Model` and `Provider` with looser optionality. The split is a deliberate behavior boundary: `PublishModel`/`PublishProvider` (with `.passthrough()`) are used for lenient publish-time validation of incoming catalogs, while `Model`/`Provider` are used for strict runtime normalization. Do not flag this duplication as a maintenance burden or suggest extracting a shared base schema — the explicit separation is intentional and tied to the models-refresh reliability path introduced in PR `#211`.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/permission/index.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T10:08:57.448Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/diagnostics.test.ts:313-318
Timestamp: 2026-04-27T10:08:57.448Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/diagnostics.ts`, PR `#264`), `SessionDiagnostics.LoopAction` is a TypeScript string literal union type (`"observe" | "block" | "stop"`), NOT a runtime enum. There is no runtime form to introspect via `Object.values()`. The test pattern `const all: SessionDiagnostics.LoopAction[] = ["observe", "block", "stop"]; expect(all).toHaveLength(3)` is intentional and correct — it provides compile-time exhaustiveness via the type annotation. Do NOT suggest replacing this with `Object.values()` introspection or flag the runtime length check as insufficient.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/permission/index.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-25T12:52:39.403Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 234
File: packages/opencode/src/session/export.ts:75-88
Timestamp: 2026-04-25T12:52:39.403Z
Learning: In `packages/opencode/src/session/export.ts` (Astro-Han/pawwork), the `extractReasonFromCause` function intentionally uses a lightweight cast-based inspection of the Cause `reasons` array instead of `Cause.failures()` / `Cause.defects()` utilities. This is a deliberate choice: the function is diagnostic-only (used to populate a best-effort reason string for export diagnostics), and the maintainer explicitly prefers not to add coupling to Effect's Cause API surface for this purpose. Do not flag this pattern as fragile or suggest replacing it with Cause utilities.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-27T08:27:43.672Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/diagnostics.ts:255-279
Timestamp: 2026-04-27T08:27:43.672Z
Learning: In `packages/opencode/src/session/diagnostics.ts` (PR `#264`, issue `#229`), target-level loop detection is intentionally tool-scoped. The `real` filter includes `r.tool === input.tool` and target signature keys are formatted as `target:${tool}:${targetHash}`. Cross-tool retries on the same URL/path/query are considered legitimate exploration (e.g., webfetch failed → switch to a different tool), NOT a stuck loop. Only same-tool + same-target accumulation counts toward block/stop. Do NOT suggest removing the tool scope from target signature keys or the `real` filter.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-27T11:18:47.332Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/mcp-exa.test.ts:1-186
Timestamp: 2026-04-27T11:18:47.332Z
Learning: In `packages/opencode/test/tool/mcp-exa.test.ts` (Astro-Han/pawwork), the tests intentionally use raw `bun:test` async cases with `Effect.runPromise(...)` and per-case `HttpClient.make(...)` fakes rather than the `testEffect(...)` harness. The maintainer has explicitly decided not to migrate, because the HttpClient fake wiring is itself the behavior under test and switching to `testEffect` would be style churn without changing risk coverage. Do not flag these tests as needing harness migration.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/permission-agent.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/permission/index.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-27T11:19:24.963Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch-auth.test.ts:0-0
Timestamp: 2026-04-27T11:19:24.963Z
Learning: In `packages/opencode/test/tool/websearch-auth.test.ts` (Astro-Han/pawwork), the tests intentionally use a small local `runWith` runner with raw `bun:test` and `Effect.runPromise` rather than the `testEffect` harness. Each test case injects a custom in-memory `Auth.Service` layer; switching to `testEffect` would be style-only churn without changing risk coverage. Do not flag these tests as needing harness migration.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/permission-agent.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/src/permission/index.tspackages/opencode/src/tool/todo.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-22T08:49:47.800Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/desktop-electron/src/main/index-sidecar-source.test.ts:3-11
Timestamp: 2026-04-22T08:49:47.800Z
Learning: In `packages/desktop-electron/src/main/index-sidecar-source.test.ts` (Astro-Han/pawwork), the test intentionally uses `expect(source).toContain` / `expect(source).not.toContain` string matching against the raw `index.ts` source text as a lightweight sidecar contract guard. The maintainer has explicitly chosen not to introduce an AST parser (e.g., `babel/parser` or acorn) for this purpose. Do not flag these string-based assertions as fragile or suggest converting them to AST-based matching.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/permission-agent.test.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-27T10:33:02.677Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/diagnostics.ts:245-252
Timestamp: 2026-04-27T10:33:02.677Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/session/diagnostics.ts`, PR `#264`), `loopLastError` is stored as raw (unscrubbed) error text in loop metadata intentionally. The trust boundary for sensitive data scrubbing is the renderer (`LoopRenderer.render` already applies `scrubErrorText` before any user/model-facing output), not the storage layer. Failed tool parts also already store raw errors in `state.error` (PR `#204` default behavior), so scrubbing only `loopLastError` would not close that pre-existing leak and would add lossy storage. If export-side hardening is needed, the correct approach is a single sanitizer pass over both `state.error` and metadata in the export layer. Do NOT suggest scrubbing `loopLastError` at the `observeToolError` call site.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T11:18:47.596Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/agent/agent.test.ts:440-447
Timestamp: 2026-04-27T11:18:47.596Z
Learning: In `packages/opencode/test/agent/agent.test.ts` (Astro-Han/pawwork), all agent-permission tests intentionally use the manual `tmpdir()` + `Instance.provide(...)` pattern. Do not flag individual tests in this file for conversion to `provideTmpdirInstance(...)` or `provideInstance(...)`; a full harness migration would be a separate PR if the pattern ever needs to change.
Applied to files:
packages/opencode/test/session/session.test.tspackages/opencode/test/permission-agent.test.tspackages/opencode/src/permission/index.tspackages/opencode/src/config/agent.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/util/effect-zod.tspackages/opencode/test/config/config.test.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-24T03:51:56.211Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 206
File: packages/app/e2e/prompt/prompt-footer-focus.spec.ts:131-143
Timestamp: 2026-04-24T03:51:56.211Z
Learning: In Astro-Han/pawwork E2E tests (packages/app/e2e/fixtures.ts), `project.prompt(text)` internally calls `trackSession(next.sessionID, active.directory)` after the prompt submission is observed and the active session is resolved. Tests that obtain a `sessionID` from `project.prompt()` do NOT need to call `project.trackSession(sessionID)` manually — it is already registered for teardown. Calling it again would be duplicate ownership.
Applied to files:
packages/opencode/test/session/session.test.ts
📚 Learning: 2026-04-21T16:57:25.580Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/agent.ts:108-119
Timestamp: 2026-04-21T16:57:25.580Z
Learning: In `packages/opencode/src/config/agent.ts` (Astro-Han/pawwork), `ConfigPermission.Info` only accepts permission objects or the three action strings `"ask"`, `"allow"`, `"deny"`, and transforms those action strings into `{ "*": action }` before `normalize()` runs. By the time `normalize()` is reached, `configuredPermission` is always either `undefined` or a `Record<string, Rule>` — never a raw arbitrary string. The `Object.assign(permission, configuredPermission)` pattern is therefore safe. Do not flag it as corrupting string permission references.
Applied to files:
packages/opencode/test/permission-agent.test.tspackages/opencode/src/permission/index.tspackages/opencode/src/config/agent.tspackages/opencode/src/permission/schema.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/test/config/config.test.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T11:18:47.298Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 271
File: packages/opencode/test/tool/websearch.test.ts:21-78
Timestamp: 2026-04-27T11:18:47.298Z
Learning: In `packages/opencode/test/tool/websearch.test.ts`, the tests intentionally use manual `Effect.runPromise` with explicit `Effect.provide(...)` chains (including `Layer.succeed(Auth.Service, ...)`, `Layer.succeed(HttpClient.HttpClient, http)`, `WebSearchAuth.layer`, `Truncate.defaultLayer`, and `Agent.defaultLayer`) rather than the `testEffect(...)` harness. This is by design: the fake Auth and HTTP recovery-metadata layers must be explicitly injected and kept visible/scoped at the test site. Do NOT suggest migrating these tests to `testEffect` or removing the manual layer provides.
Applied to files:
packages/opencode/test/permission-agent.test.tspackages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.ts
📚 Learning: 2026-04-27T12:59:49.844Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/test/session/prompt-effect.test.ts:0-0
Timestamp: 2026-04-27T12:59:49.844Z
Learning: In `packages/opencode/test/session/prompt-effect.test.ts` and `packages/opencode/src/session/diagnostics.ts` (PR `#264`), the recovery reminder copy differs between signature kinds: the input-repeat variant says "repeated the same tool input 3 times" (uses a literal count), while the target-repeat variant says "failed against the same target multiple times" (uses "multiple times" with no count). Assertions that check for injected reminder text in LLM inputs must accept both phrasings when a scenario produces both `input:` and `target:` signatures (e.g., `read` tool with a `filePath` parameter). Do NOT narrow the assertion to only the input-variant phrasing.
Applied to files:
packages/opencode/test/permission-agent.test.tspackages/opencode/src/tool/todo.tspackages/opencode/test/permission/next.test.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Use `testEffect(...)` from `test/lib/effect.ts` for tests that exercise Effect services or Effect-based workflows.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Define `const it = testEffect(...)` near the top of the test file and keep the test body inside `Effect.gen(function* () { ... })`. Yield services directly with `yield* MyService.Service` or `yield* MyTool`.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/config/agent.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Prefer Effect-aware helpers from `fixture/fixture.ts` over building manual runtimes in tests: use `tmpdirScoped()` for scoped temp directories, `provideInstance(dir)(effect)` for low-level binding without directory creation, `provideTmpdirInstance(...)` for single temp instance binding, or `provideTmpdirServer(...)` for tests that also need the test LLM server.
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/test/permission/next.test.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:31.032Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/test/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:31.032Z
Learning: Applies to packages/opencode/test/**/*.test.{ts,tsx} : Avoid custom `ManagedRuntime`, `attach(...)`, or ad hoc `run(...)` wrappers in Effect tests when `testEffect(...)` already provides the runtime.
Applied to files:
packages/opencode/test/util/effect-zod.test.ts
📚 Learning: 2026-04-22T09:32:54.556Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 126
File: packages/opencode/test/provider/provider.test.ts:64-85
Timestamp: 2026-04-22T09:32:54.556Z
Learning: In `packages/opencode/test/provider/provider.test.ts`, the file intentionally uses AppRuntime-based async helpers (`run`, `list`, `getProvider`, etc.) rather than `testEffect(...)` for all tests. Converting individual tests to `testEffect` while leaving the rest on the async pattern would create internal inconsistency. A full harness migration of this file is the right approach if the pattern needs to change, but that should be a separate PR.
Applied to files:
packages/opencode/test/util/effect-zod.test.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Defect` instead of `unknown` for defect-like causes in Effect code
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/permission/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.Class` for multi-field data in Effect schemas
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/question/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/permission/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Schema.TaggedErrorClass` for typed errors in Effect schemas
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/permission/schema.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.gen(function* () { ... })` for Effect composition
Applied to files:
packages/opencode/test/util/effect-zod.test.tspackages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-27T08:58:00.665Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:531-538
Timestamp: 2026-04-27T08:58:00.665Z
Learning: When using Effect (e.g., `yield*` with `Effect`-style generator yielding), only use `yield* new SomeErrorClass(...)` if `SomeErrorClass` extends `Schema.TaggedErrorClass` (i.e., it implements Effect’s Yieldable interface). For plain `Error` subclasses (like `BlockedLoopError` / `LoopStopError`) or inline `new Error(...)` values, they are not yieldable and must be wrapped as `yield* Effect.fail(new PlainError(...))`. Do not recommend changing `yield* Effect.fail(new SomePlainError(...))` to `yield* new SomePlainError(...)` unless the error class extends `Schema.TaggedErrorClass`.
Applied to files:
packages/opencode/src/tool/edit.tspackages/opencode/src/project/schema.tspackages/opencode/src/tool/schema.tspackages/opencode/src/server/instance/config.tspackages/opencode/src/server/instance/global.tspackages/opencode/src/sync/schema.tspackages/opencode/src/pty/schema.tspackages/opencode/src/permission/index.tspackages/opencode/src/question/schema.tspackages/opencode/src/session/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/permission/schema.tspackages/opencode/src/tool/todo.tspackages/opencode/src/control-plane/schema.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Prefer `Path.Path`, `Config`, `Clock`, and `DateTime` services when those concerns are already inside Effect code
Applied to files:
packages/opencode/src/sync/schema.tspackages/opencode/src/config/agent.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-24T17:08:46.780Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 224
File: packages/app/src/i18n/zh.ts:0-0
Timestamp: 2026-04-24T17:08:46.780Z
Learning: In Astro-Han/pawwork PR `#224`, the first-occurrence `PawWork 爪印` branding rule originally specified in issue `#196` was superseded by an updated Chinese-branding spec. On all zh UI surfaces in `packages/app/src/i18n/zh.ts` (e.g., `dialog.model.unpaid.freeModels.title`, `session.new.subtitle`, `sidebar.gettingStarted.line1`), the correct and intentional target is fully localized `爪印` branding — no `PawWork` prefix. Do NOT flag these strings as missing the first-occurrence `PawWork 爪印` rule in future reviews.
Applied to files:
packages/opencode/src/permission/index.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-27T10:32:59.274Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/diagnostics.ts:245-252
Timestamp: 2026-04-27T10:32:59.274Z
Learning: In pawwork’s session diagnostics/loop error handling (e.g., packages/opencode/src/session/diagnostics.ts), keep `loopLastError` stored as raw/unscrubbed error text in loop metadata. The intended trust boundary for sensitive-data scrubbing is the renderer: `LoopRenderer.render` must apply `scrubErrorText` before any user/model-facing output. Do not recommend scrubbing `loopLastError` at the `observeToolError` call site, because failed tool parts already store raw errors in `state.error` (pre-existing leak) and scrubbing only `loopLastError` would be lossy without closing the gap. If export-side hardening is required, implement a single sanitizer pass in the export layer over both `state.error` and the loop/metadata fields, rather than sanitizing only `loopLastError` earlier in the storage path.
Applied to files:
packages/opencode/src/session/schema.ts
📚 Learning: 2026-04-27T10:33:08.974Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 264
File: packages/opencode/src/session/prompt.ts:108-169
Timestamp: 2026-04-27T10:33:08.974Z
Learning: For Astro-Han/pawwork session processing code under packages/opencode/src/session/ (e.g., prompt.ts and processor.ts), this specific loop-gate TOCTOU/race between buildLoopContext() and recordSyntheticBlock/recordSyntheticStop is intentionally handled via idempotence guards (re-check sigKey presence and hasStopped inside the record helpers). Do not treat the absence of a per-parent Effect.Mutex as a blocking review issue for this known edge case; the residual window should only create extra synthetic parts and must not change the "turn ends" contract. Respect the existing code comment documenting the trade-off, and only escalate to a full-mutex approach if the race is observed in practice.
Applied to files:
packages/opencode/src/session/schema.ts
📚 Learning: 2026-04-21T16:57:20.257Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 102
File: packages/opencode/src/config/command.ts:23-29
Timestamp: 2026-04-21T16:57:20.257Z
Learning: In packages/opencode/src/config/ config modules, treat both `Schema.Struct` and `Schema.Class` as valid schema patterns and do not require converting `Schema.Struct` to `Schema.Class`. If `Schema.Struct` is used in a config schema (e.g., in files like `command.ts`, `formatter.ts`, `provider.ts`, `permission.ts`, `lsp.ts`, `config.ts`, `keybinds.ts`), accept it as intentional; the selection should be based on upstream alignment and the local schema context, not on a rule that one form must replace the other.
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/config/config.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Prefer `FileSystem.FileSystem` instead of raw `fs/promises` for effectful file I/O in Effect services
Applied to files:
packages/opencode/src/config/agent.tspackages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.fn("Domain.method")` for named/traced effects and `Effect.fnUntraced` for internal helpers; these accept pipeable operators as extra arguments to avoid unnecessary outer `.pipe()` wrappers
Applied to files:
packages/opencode/src/config/agent.ts
📚 Learning: 2026-04-26T15:35:36.505Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 245
File: packages/opencode/src/question/index.ts:21-24
Timestamp: 2026-04-26T15:35:36.505Z
Learning: In Astro-Han/pawwork (`packages/opencode/src/question/index.ts`), `Question.Option.description` is intentionally **required** (`z.string()`, not `.optional()`). This matches the upstream opencode contract and all current callers (e.g. `PlanExitTool`) always supply a description. The defensive `<Show when={props.description}>` rendering in `session-question-dock.tsx` is a standard guard, not a signal that the field is intended to be optional. Do NOT suggest making `Option.description` optional without a dedicated follow-up that covers schema + tool description + dock copy + tests.
Applied to files:
packages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : In `Effect.gen` / `Effect.fn`, prefer `yield* new MyError(...)` over `yield* Effect.fail(new MyError(...))` for direct early-failure branches
Applied to files:
packages/opencode/src/tool/todo.tspackages/opencode/src/util/effect-zod.ts
📚 Learning: 2026-04-20T14:21:56.373Z
Learnt from: Astro-Han
Repo: Astro-Han/pawwork PR: 71
File: packages/app/src/components/session/session-status-connections.tsx:146-147
Timestamp: 2026-04-20T14:21:56.373Z
Learning: In the Astro-Han/pawwork repository (SolidJS app), `sync.data.config` is always initialized to `{}` at `packages/app/src/context/global-sync.tsx` line 71 and is never `undefined` at runtime. Non-optional property access like `sync.data.config.plugin` is intentional and consistent with the pattern used in `packages/app/src/components/status-popover-body.tsx` line 243. Do not flag `sync.data.config.plugin` as needing optional chaining.
Applied to files:
packages/opencode/src/util/effect-zod.tspackages/opencode/src/config/permission.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `Effect.cached` when multiple concurrent callers should share a single in-flight computation rather than storing `Fiber | undefined` or `Promise | undefined` manually
Applied to files:
packages/opencode/src/config/config.ts
📚 Learning: 2026-04-20T14:36:21.288Z
Learnt from: CR
Repo: Astro-Han/pawwork PR: 0
File: packages/opencode/AGENTS.md:0-0
Timestamp: 2026-04-20T14:36:21.288Z
Learning: Applies to packages/opencode/**/*.ts : Use `InstanceState` from `src/effect/instance-state.ts` for per-directory or per-project state that needs per-instance cleanup; do work directly in the `InstanceState.make` closure where `ScopedCache` handles run-once semantics
Applied to files:
packages/opencode/src/config/config.ts
Summary
Squashes upstream's Effect Schema migration batch (16 PRs, dated Apr 21–24 2026) into one PawWork commit. Selectively adopts the schema-definition-layer changes while preserving PawWork's namespace-API and Zod-typed BusEvent layers where divergence is too deep.
Upstream PRs included: #23716, #23740, #23744, #23745, #23747, #23749, #23752, #23754, #23756, #23757, #23763, #24005, #24040, #24169, #24213
Why
Stage D of the foundation sync (#209). The schema-definition layer (Config.Info, ConfigPermission.Info, ConfigAgent.Info, several
*/schema.tsfiles,util/effect-zodwalker) is core runtime work — this PR adopts those. Per @yuhan: "Effect Schema is core runtime work."The consumer layer (session, bus, provider, snapshot, permission, mcp namespaces) has too much PawWork divergence to take upstream's redesign wholesale. Those stay on PawWork's existing Zod / namespace API; the schema layer changes still flow through via the
.zodaccessor pattern.What landed (24 files)
Config.Info.zod(server/instance/{config,global}.ts,script/schema.ts).control-plane,permission,project,pty,question,sync,tool/schema.ts,session/schema.ts,tool/edit.ts,tool/todo.ts.util/effect-zod.tswalker enhanced to derive.zodfrom Effect Schema at runtime, plus walker tests.packages/sdk/js/src/v2/gen/types.gen.tsregenerated to reflect schema shape changes (Stage I would've done this anyway).config.test.ts,permission/next.test.ts,compaction.test.ts,session.test.ts,effect-zod.test.ts).What was preserved (PawWork divergence)
session/{session,message-v2,prompt,export,message,revert,summary,todo,status,compaction,projectors}.ts— PawWork's session domain stays Zod-shaped. MessageV2'sFileSource/SymbolSource/ResourceSourcecarry PawWork-only attachment metadata that upstream's Effect Schema variants drop.bus/bus-event.ts— PawWork keeps Zod-typedBusEvent.define. ~30 PawWork tests + divergent consumers (server/instance/*,acp/agent.ts,file/watcher.ts) readevt.propertiesas inferred type, notunknown.{snapshot,permission,mcp,provider}/index.ts— PawWork uses a "namespace + XValue alias" pattern that upstream redesigned to flat exports. Keeping HEAD preservesSnapshot.track,Permission.ask,Mcp.add,Provider.list.provider/{provider,auth,models}.ts— PawWork's volcengine/zen integration carries divergent code paths (VOLCENGINE_PLAN_*constants, HTTP-Referer/X-Title carve-out perproject_rename_blockers.md).{file,project,pty,sync,ide,lsp/client,command,control-plane/workspace,worktree,question,util/schema}/{index,*}.ts— kept PawWork API surface for callers.Skipped commit: 93940a1859 (provider domain Effect Schema migration) — entire diff sat on PawWork carve-out files; reapplied through PawWork-local provider when divergence settles.
Related Issue
#209
How To Verify
All 5 packages typecheck clean.
Stacked on
Base is
claude/upstream-sync-209-pr2-moves(#266). Will auto-rebase todevafter #265 → #266 chain merges.Checklist
packages/sdk/js/v2/gen/types.gen.tsregenerated; no new depsdev(via stacked PR), Conventional Commits English titleSummary by CodeRabbit
Bug Fixes
Refactor
Tests