[PR #13706] refactor: consolidate Question/Permission shared patterns into Pending module #14785

Open
opened 2026-02-16 18:19:32 -05:00 by yindo · 0 comments
Owner

Original Pull Request: https://github.com/anomalyco/opencode/pull/13706

State: open
Merged: No


Summary

  • Extract shared "ask → reply/reject → resolve promise" pattern from PermissionNext and Question into a thin Pending module (src/pending/index.ts)
  • Unify RejectedError as a shared base class — both PermissionNext.RejectedError and Question.RejectedError now extend Pending.RejectedError
  • Delete orphaned legacy Permission namespace (zero imports, fully dead code)
  • Rename permission/next.tspermission/index.ts and update all 21 import paths

Motivation

PermissionNext and Question both independently implement an identical pending-request lifecycle: store {info, resolve, reject} → publish Bus event → return Promise → resolve/reject on reply. Both define their own RejectedError class, and processor.ts catches both with a dual instanceof check. This PR extracts the shared surface into a Pending module to eliminate the conceptual duplication.

What changed

New: src/pending/index.ts

Thin shared module with:

  • Pending.RejectedError — base class with optional message parameter
  • Pending.Entry<Info, ResolveValue> — generic type for pending records

Refactored: src/permission/index.ts (renamed from next.ts)

  • RejectedError extends Pending.RejectedError (preserves domain message)
  • Pending record typed as Pending.Entry<Request>
  • CorrectedError and DeniedError unchanged (domain-specific, extend Error)
  • All .meta({ ref }) OpenAPI schema names unchanged
  • All Bus event strings unchanged
  • No behavioral changes to rule evaluation or cascade logic

Refactored: src/question/index.ts

  • RejectedError extends Pending.RejectedError (preserves domain message)
  • Pending record typed as Pending.Entry<Request, Answer[]>
  • All .meta({ ref }) and event strings unchanged

Simplified: src/session/processor.ts

  • Dual instanceof check → single instanceof Pending.RejectedError
  • Removed unused Question import

Deleted: src/permission/index.ts (old)

  • Legacy Permission namespace with zero imports — fully orphaned dead code

What did NOT change

  • All OpenAPI .meta({ ref }) values (PermissionAction, PermissionRule, PermissionRuleset, PermissionRequest, QuestionOption, QuestionInfo, QuestionRequest, QuestionAnswer)
  • All Bus event type strings (permission.asked, permission.replied, question.asked, question.replied, question.rejected)
  • CorrectedError / DeniedError (domain-specific, stay in Permission namespace)
  • fn() Zod validation wrapper on PermissionNext.ask
  • Permission cascade logic in reply()
  • arity.ts (independent bash parsing utility)
  • UI components (QuestionPrompt, PermissionPrompt)
  • Route handlers (routes/question.ts, routes/permission.ts)

Testing

  • 9 new tests in test/pending/pending.test.ts:
    • Pending.RejectedError default/custom message, instanceof Error
    • Pending.Entry type compilation verification
    • instanceof chain: PermissionNext.RejectedError / Question.RejectedErrorPending.RejectedError
    • CorrectedError / DeniedError → NOT Pending.RejectedError
  • Full suite: 984 pass, 3 pre-existing failures (unchanged)
  • bun tsc --noEmit: zero errors
**Original Pull Request:** https://github.com/anomalyco/opencode/pull/13706 **State:** open **Merged:** No --- ## Summary - Extract shared "ask → reply/reject → resolve promise" pattern from `PermissionNext` and `Question` into a thin `Pending` module (`src/pending/index.ts`) - Unify `RejectedError` as a shared base class — both `PermissionNext.RejectedError` and `Question.RejectedError` now extend `Pending.RejectedError` - Delete orphaned legacy `Permission` namespace (zero imports, fully dead code) - Rename `permission/next.ts` → `permission/index.ts` and update all 21 import paths ## Motivation `PermissionNext` and `Question` both independently implement an identical pending-request lifecycle: store `{info, resolve, reject}` → publish Bus event → return Promise → resolve/reject on reply. Both define their own `RejectedError` class, and `processor.ts` catches both with a dual `instanceof` check. This PR extracts the shared surface into a `Pending` module to eliminate the conceptual duplication. ## What changed ### New: `src/pending/index.ts` Thin shared module with: - `Pending.RejectedError` — base class with optional message parameter - `Pending.Entry<Info, ResolveValue>` — generic type for pending records ### Refactored: `src/permission/index.ts` (renamed from `next.ts`) - `RejectedError extends Pending.RejectedError` (preserves domain message) - Pending record typed as `Pending.Entry<Request>` - `CorrectedError` and `DeniedError` unchanged (domain-specific, extend `Error`) - All `.meta({ ref })` OpenAPI schema names unchanged - All Bus event strings unchanged - No behavioral changes to rule evaluation or cascade logic ### Refactored: `src/question/index.ts` - `RejectedError extends Pending.RejectedError` (preserves domain message) - Pending record typed as `Pending.Entry<Request, Answer[]>` - All `.meta({ ref })` and event strings unchanged ### Simplified: `src/session/processor.ts` - Dual `instanceof` check → single `instanceof Pending.RejectedError` - Removed unused `Question` import ### Deleted: `src/permission/index.ts` (old) - Legacy `Permission` namespace with zero imports — fully orphaned dead code ## What did NOT change - All OpenAPI `.meta({ ref })` values (PermissionAction, PermissionRule, PermissionRuleset, PermissionRequest, QuestionOption, QuestionInfo, QuestionRequest, QuestionAnswer) - All Bus event type strings (`permission.asked`, `permission.replied`, `question.asked`, `question.replied`, `question.rejected`) - `CorrectedError` / `DeniedError` (domain-specific, stay in Permission namespace) - `fn()` Zod validation wrapper on `PermissionNext.ask` - Permission cascade logic in `reply()` - `arity.ts` (independent bash parsing utility) - UI components (`QuestionPrompt`, `PermissionPrompt`) - Route handlers (`routes/question.ts`, `routes/permission.ts`) ## Testing - 9 new tests in `test/pending/pending.test.ts`: - `Pending.RejectedError` default/custom message, instanceof Error - `Pending.Entry` type compilation verification - instanceof chain: `PermissionNext.RejectedError` / `Question.RejectedError` → `Pending.RejectedError` ✓ - `CorrectedError` / `DeniedError` → NOT `Pending.RejectedError` ✓ - Full suite: 984 pass, 3 pre-existing failures (unchanged) - `bun tsc --noEmit`: zero errors
yindo added the pull-request label 2026-02-16 18:19:32 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#14785