diff --git a/ts/EFFECT_NATIVE_REWRITE_AUDIT.md b/ts/EFFECT_NATIVE_REWRITE_AUDIT.md index 7949973d..1f8e1276 100644 --- a/ts/EFFECT_NATIVE_REWRITE_AUDIT.md +++ b/ts/EFFECT_NATIVE_REWRITE_AUDIT.md @@ -13,7 +13,7 @@ Verified source roots: - Installed Effect beta used by this workspace: `ts/node_modules/effect` Current signal counts from `ts/packages` after the 2026-06-02 Text completion -generator boundary slice: +provider status narrowing slice: | Signal | Count | | --- | ---: | @@ -76,6 +76,8 @@ Notes: `Effect.runPromise(Effect.fail(...))` fallback and the related `AsyncGenerator`/`IteratorResult` assertions from `model/text-completion/common.ts`. +- The text completion provider status slice replaced manual status/statusCode + record assertions with `effect/Predicate` narrowing. - `Record` and `throwLibrarianServiceError` are now clean in `ts/packages`. @@ -589,6 +591,24 @@ Notes: - `cd ts && bun run test` - `git diff --check` +### 2026-06-02: Text Completion Provider Status Narrowing Slice + +- Status: migrated and root-verified. +- Completed: + - `ts/packages/flow/src/model/text-completion/common.ts` now uses + `effect/Predicate` narrowing for provider `status` / `statusCode` + inspection instead of local record assertions. + - `ts/packages/flow/src/__tests__/text-completion-common.test.ts` covers + both rate-limit status fields. +- Verification: + - `bun run --cwd ts/packages/flow test -- src/__tests__/text-completion-common.test.ts` + - `bun run --cwd ts/packages/flow build` + - `cd ts && bun run check` + - `bun run --cwd ts/packages/flow test` + - `cd ts && bun run build` + - `cd ts && bun run test` + - `git diff --check` + ## Subagent Findings To Preserve - MCP/workbench: diff --git a/ts/packages/flow/src/__tests__/text-completion-common.test.ts b/ts/packages/flow/src/__tests__/text-completion-common.test.ts index 8001fc47..71e484ac 100644 --- a/ts/packages/flow/src/__tests__/text-completion-common.test.ts +++ b/ts/packages/flow/src/__tests__/text-completion-common.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "@effect/vitest"; import type { LlmChunk } from "@trustgraph/base"; -import { providerRuntimeError, toAsyncGenerator } from "../model/text-completion/common.js"; +import { providerRuntimeError, providerStatusError, toAsyncGenerator } from "../model/text-completion/common.js"; const emptyChunkIterator = (): AsyncIterable => ({ [Symbol.asyncIterator]: () => ({ @@ -9,6 +9,18 @@ const emptyChunkIterator = (): AsyncIterable => ({ }); describe("text completion common helpers", () => { + it("maps provider rate-limit status fields to tagged retry errors", () => { + expect(providerStatusError("test-provider", { status: 429 })).toMatchObject({ + _tag: "TooManyRequestsError", + message: "Rate limit exceeded", + }); + + expect(providerStatusError("test-provider", { statusCode: 429 })).toMatchObject({ + _tag: "TooManyRequestsError", + message: "Rate limit exceeded", + }); + }); + it("maps fallback generator throw failures into tagged provider errors", async () => { const generator = toAsyncGenerator( emptyChunkIterator(), diff --git a/ts/packages/flow/src/model/text-completion/common.ts b/ts/packages/flow/src/model/text-completion/common.ts index b512ecea..9cfd1774 100644 --- a/ts/packages/flow/src/model/text-completion/common.ts +++ b/ts/packages/flow/src/model/text-completion/common.ts @@ -5,6 +5,7 @@ import { } from "@trustgraph/base"; import { Config, Effect } from "effect"; import * as O from "effect/Option"; +import * as Predicate from "effect/Predicate"; import * as S from "effect/Schema"; export class TextCompletionConfigError extends S.TaggedErrorClass()( @@ -68,11 +69,11 @@ export const providerStatusError = ( provider: string, error: unknown, ): TextCompletionRuntimeError => { - const status = typeof error === "object" && error !== null && "status" in error - ? (error as { readonly status?: unknown }).status + const status = Predicate.isObject(error) && Predicate.hasProperty(error, "status") + ? error.status : undefined; - const statusCode = typeof error === "object" && error !== null && "statusCode" in error - ? (error as { readonly statusCode?: unknown }).statusCode + const statusCode = Predicate.isObject(error) && Predicate.hasProperty(error, "statusCode") + ? error.statusCode : undefined; return status === 429 || statusCode === 429 ? TooManyRequestsError.make({ message: "Rate limit exceeded" })