Skip to content

chore: adopt @unthrown/oxlint recommended rules - #356

Merged
btravers merged 3 commits into
mainfrom
chore/unthrown-oxlint
Jul 30, 2026
Merged

chore: adopt @unthrown/oxlint recommended rules#356
btravers merged 3 commits into
mainfrom
chore/unthrown-oxlint

Conversation

@btravers

Copy link
Copy Markdown
Collaborator

Summary

Registers the @unthrown/oxlint plugin with the four recommended rules (no-ambiguous-error-type, no-catch-all-pattern, no-unhandled-result, prefer-async-result; the opt-ins no-throw/prefer-ensure stay off — the worker's sanctioned ValidationError throws would fight no-throw). @unthrown/oxlint joins the first-party minimumReleaseAgeExclude list (published inside the 7-day supply-chain gate; same treatment as unthrown and @unthrown/vitest).

The rules found 29 violations; 24 real fixes, 2 targeted disables:

  • Genuine public-type imprecision caught by no-ambiguous-error-type: the exported ClientInferSignal/Query/Update aliases claimed AsyncResult<..., Error> while the proxies actually produce {Signal,Query,Update}ValidationError | WorkflowExecutionNotFoundError. The aliases now carry the concrete unions, and the mapped conditionals in client.ts that papered over the lie collapsed to identity — TypedWorkflowHandle now references the aliases directly.
  • prefer-async-result: 16 internal work-thunk Promise<Result> annotations dropped (with explicit type arguments pinned at makeAsyncResult call sites where TS can't collapse per-return Result unions); 3 named async helpers converted to non-async AsyncResult-returning functions (behavior proven unchanged — callers' assertNoDefect already re-threw into the enclosing throw→defect net).
  • 2 disables, both on the sanctioned Promise→AsyncResult conversion seam (_internal_makeAsyncResult and its client re-export), each with the reason inline.
  • Spec fixtures' AsyncResult<unknown, unknown> replaced with the concrete ProxyError union the code actually produces.

Cherry-picked from the post-#354 branch onto main. Mirror adoption in amqp-contract: btravstack/amqp-contract#613.

Verification

pnpm lint clean; pnpm turbo run typecheck test 16/16 tasks; per-package: client 140, worker 182, contract 120, testing 19 tests passing.

🤖 Generated with Claude Code

Register the @unthrown/oxlint plugin (catalog 5.0.0, root devDep,
minimumReleaseAgeExclude entry) with the 4 recommended rules and fix
the 29 violations it surfaced:

- no-ambiguous-error-type (client/src/types.ts): genuine findings — the
  ClientInferSignal/Query/Update aliases claimed AsyncResult<..., Error>
  while the handle proxies actually produce the precise
  {Signal,Query,Update}ValidationError | WorkflowExecutionNotFoundError
  unions; the aliases now carry those unions, and TypedWorkflowHandle's
  queries/signals/updates use them directly instead of pattern-matching
  the bare Error away (which also removes the three
  `AsyncResult<infer R, Error>` conditionals in client.ts).
- no-ambiguous-error-type (worker/src/activities-proxy.spec.ts): the
  test fixtures now use the concrete
  AnyContractError | ActivityError | ActivityCancelledError union the
  Result-shaped proxy really returns.
- prefer-async-result (work thunks feeding makeAsyncResult in client.ts,
  schedule.ts, cancellation.ts, child-workflow.ts, testing/activity.ts):
  dropped the Promise<Result<...>> return annotations; where TS cannot
  collapse the thunk's per-return Result union on its own, the T/E pair
  is pinned as explicit type arguments on the makeAsyncResult call.
- prefer-async-result (async validation helpers
  resolveDefinitionAndValidateInput / getAndValidateChildWorkflow /
  validateChildWorkflowOutput): converted to non-async functions
  returning AsyncResult via makeAsyncResult — behavior is unchanged
  because callers already assertNoDefect (re-throwing a captured defect
  into the enclosing throw→defect net).
- prefer-async-result (contract/src/result-async.ts and its
  client-internal re-export wrapper): kept with targeted
  oxlint-disable-next-line — these ARE the Promise→AsyncResult
  conversion seam whose thunk rejection becomes the defect, and an async
  implementer cannot be annotated AsyncResult.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adopts the @unthrown/oxlint plugin and enables its recommended rules to enforce more precise AsyncResult/Result usage and error unions across the Temporal contract client/worker/testing packages.

Changes:

  • Register @unthrown/oxlint in .oxlintrc.json and add the dependency via the workspace catalog (including supply-chain gate exclusion).
  • Tighten the client public type surface by replacing AsyncResult<..., Error> with concrete validation/not-found error unions, and simplify TypedWorkflowHandle mapped types accordingly.
  • Refactor several internal helpers from Promise<Result<...>> to AsyncResult<...> (and remove now-redundant Promise<Result> annotations), plus align test fixtures with the real proxy error union.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated no comments.

Show a summary per file
File Description
pnpm-workspace.yaml Adds @unthrown/oxlint to the catalog and supply-chain exclusion list.
pnpm-lock.yaml Locks @unthrown/oxlint (and its transitive deps) into the workspace.
package.json Adds @unthrown/oxlint as a root devDependency via the catalog.
.oxlintrc.json Registers the unthrown plugin and enables the recommended rules.
packages/contract/src/result-async.ts Documents and disables prefer-async-result at the intentional Promise→AsyncResult seam.
packages/client/src/internal.ts Documents and disables prefer-async-result at the client’s Promise→AsyncResult seam wrapper.
packages/client/src/types.ts Replaces ambiguous Error error types with concrete validation/not-found unions for signals/queries/updates.
packages/client/src/client.ts Simplifies TypedWorkflowHandle query/signal/update types now that aliases carry correct unions; converts resolver helper to AsyncResult.
packages/client/src/schedule.ts Removes explicit Promise<Result<...>> annotations and pins makeAsyncResult generics at call sites.
packages/worker/src/child-workflow.ts Converts validation helpers to AsyncResult and pins makeAsyncResult generics where inference can’t collapse unions.
packages/worker/src/cancellation.ts Removes explicit Promise<Result<...>> annotations and pins makeAsyncResult generics.
packages/worker/src/activities-proxy.spec.ts Replaces AsyncResult<unknown, unknown> with a concrete proxy error union in fixtures.
packages/testing/src/activity.ts Removes explicit Promise<Result<...>> annotation in the internal run wrapper while preserving behavior.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

Docs build (root cause of the Package / ci Build / Bundle Size failures):
the TSDoc on TypedWorkflowHandle's queries/signals/updates properties held
inline code spans containing a raw `|` (e.g.
AsyncResult<T, QueryValidationError | WorkflowExecutionNotFoundError>).
typedoc renders member docs into markdown table cells without escaping
pipes inside code spans, so markdown-it split the row at the pipe, the
broken span left `Promise<T>` as a raw HTML-looking `<T>` element, and
VitePress's Vue compiler failed with "Element is missing end tag" at
api/client/index.md. Fixed at the source by rewording the three doc
comments so no code span carries a pipe; generated files untouched.

Lint: turn on unthrown/no-throw and unthrown/prefer-ensure, so all six
rules are now at "error". prefer-ensure surfaced 0 violations. no-throw
surfaced 148:

- 76 in test files, handled by a config override turning only no-throw
  off for **/*.spec.ts and **/__tests__/** (tests throw to simulate
  defects and assert rejections; the other five rules stay on).
- 72 in source, each carrying a targeted oxlint-disable naming its
  category — none could be restructured to Err(...) without changing
  semantics:
  - 33 sanctioned ValidationError / ContractMisuseError /
    ApplicationFailure sites (CLAUDE.md rule 2 exception — Temporal's
    terminal-failure semantics require the throw)
  - 12 client-side technical faults thrown inside makeAsyncResult work
    thunks (RuntimeClientError/TechnicalError — the throw IS the
    defect-channel routing)
  - 6 defect-cause / workflow-sandbox rethrows (unmodeled failures kept
    on the defect channel)
  - 1 cancellation rethrow (CancelledFailure must propagate)
  - 7 declaration-time fail-fast config errors (worker startup /
    contract definition / test setup)
  - 13 example-domain throws in plain Promise helpers wrapped once at
    the activity boundary via fromPromise(..., qualifyFailure(...))

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 30 out of 31 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Generated file
Comments suppressed due to low confidence (1)

.oxlintrc.json:20

  • PR description says the opt-in rules unthrown/no-throw and unthrown/prefer-ensure should stay off, but .oxlintrc.json enables both as error (and even adds an override to partially relax no-throw). This makes the lint config disagree with the stated intent and is why so many oxlint-disable-next-line unthrown/no-throw directives were introduced.
  "rules": {
    "unthrown/no-ambiguous-error-type": "error",
    "unthrown/no-catch-all-pattern": "error",
    "unthrown/no-throw": "error",
    "unthrown/no-unhandled-result": "error",
    "unthrown/prefer-async-result": "error",
    "unthrown/prefer-ensure": "error"
  },

@btravers
btravers merged commit 5638ae4 into main Jul 30, 2026
12 checks passed
@btravers
btravers deleted the chore/unthrown-oxlint branch July 30, 2026 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants