Repository navigation
chore(deps): upgrade effect to 4.0.2 - #6999
Conversation
Drop the rc.112 pnpm patches: the stdin EPIPE listener is covered upstream since rc.113 (effect PRs 7712 and 7714), and the vitest runTest rewrite is superseded by upstream's finalizer ordering fix (effect PR 8154). Effect no longer depends on msgpackr, so its build approval goes too. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Generated with:
grep -rl 'effect/unstable/' apps packages tools --include='*.ts' \
| xargs sed -i '' 's#\(["'\''\]\)effect/unstable/#\1effect/#g'
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…scalCase 4.0 names Flag.string/boolean/integer/choice/choiceWithValue, Argument.string/choice, Primitive.boolean/integer/keyValuePair, GlobalFlag.setting, and Config.string/redacted/literals became String/Boolean/Int/Literals/ChoiceWithValue, KeyValuePair, Setting, and Redacted in effect 4.0.0-rc.113 (effect PRs 7453, 8121). Generated with a perl word-boundary substitution over apps, packages, and tools. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ss-property option SchemaAST.Union.mode moved under options (effect PR 8131) and ToJsonSchemaOptions.additionalProperties became onExcessProperty (effect PR 8147). The generated document is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… Socket APIs Server addresses are NetAddress.SocketAddress values (effect PR 7524), File.seek and File.Info.size use bigint and ByteSize (effect PR 8108), Encoding moved to effect/encoding/Base64Url (effect PR 8356), and Socket.runString was replaced by the reader and writer pair (effect PR 7487). The Proxy comment now describes the listener contract instead of naming a release candidate. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ol idle timeout Parse-option annotations no longer influence decoding (effect PR 8131), so the initialization command and runCommand payload schemas reject unknown keys through a Record filter ahead of the struct decode. @effect/sql-pg 4.0.1 raised the default pool idle timeout from 10 to 60 seconds (effect PR 8679); the stack's Postgres layers pin the previous value. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ient @effect/sql-pg 4.0 is a native client and dropped PgClient.fromPool (effect PR 7426). The remote session keeps its node-postgres pool for the zero idle timeout, the per-connection role step-down hook, and raw COPY connections, so a local SqlClient.make adapter over that pool replaces the removed bridge with the same statement execution, cancellation, and error classification. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ptions Encoding moved to effect/encoding/Base64Url (effect PR 8356), the Int and Finite primitives replaced Integer and Float (effect PR 8121), File.Info.size is a ByteSize, and onExcessProperty "preserve" was removed in favour of the default "ignore" (effect PR 8131). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…0 paths Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…der effect 4.0 Schema.toJsonSchemaDocument now exports an isPattern check only when its RegExp uses the Unicode flag (effect PR 8482); without it the compute and function name patternProperties selectors and the env() pattern disappeared from the published schema. The flag does not change what these ASCII patterns accept. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…fect 4.0 Schema.toJsonSchemaDocument now leaves unmodeled properties open by default (effect PR 8147); passing onExcessProperty "error" restores additionalProperties false on every struct in the published schema.json. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Upstream's finalizer ordering fix (effect PR 8154) covers the abort hook, so the patch now only adds what the stack's effect-timeout tests assert: rejecting with the pretty errors themselves and reporting a finalizer failure after a timeout. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… wording Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@effect/sql-pg 4.0 runs every query through the extended protocol, which rejects multi-command strings (effect PR 7426). The realtime schema bootstrap runs as two statements, and the generated ALTER ROLE and ALTER DATABASE batches come back from Postgres as an array and execute one at a time inside the same transaction. Tests that seeded fixtures through the client do the same. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… effect 4.0 The 4.0 generator writes a boolean additionalProperties on pattern-keyed records and renders an empty struct as not-null (effect PRs 8131 and 8147), neither of which has a per-schema setting. Both published documents now go through one assembler that leaves pattern-keyed records open, and empty bucket tables are modelled as an object-or-array union so they render as before. schema.json and project-schema.json match the develop build byte for byte. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Re-applies the effect 4.0 import path and constructor rewrites to the files develop changed, adapts the new TCP listener to NetAddress, the helper scope to Scope.Closeable, the scheduler probe to fiber.cache, and the cron test to the scoped listen queue with one statement per query. Generated with AI Co-Authored-By: Claude <noreply@anthropic.com>
Patch release only; the vitest runner patch is re-keyed to 4.0.2 with the same content, and the stack functions bootstrap bundle is regenerated after the merge. Generated with AI Co-Authored-By: Claude <noreply@anthropic.com>
The native client rejects batched statements and decodes net.http_get's bigint request id as a bigint, so the test casts it to text. Generated with AI Co-Authored-By: Claude <noreply@anthropic.com>
….0.2 4.0.2 still removes the stdin sink's error listener before the final end() flush, so a child that exits before reading its input raises EPIPE as an uncaught exception on Linux. Effect PRs 7712 and 7714 did not cover this path.
The 4.0.2 schema codegen emits code-point length checks and Unicode-flagged patterns.
The excess-key check encodes through an Unknown record, which the RPC JSON codec rejects when a nested optional field is explicitly undefined. Encoding now drops undefined entries, as the plain struct codec did.
The reset e2e fixture runs one statement per query and reads count(*) as int, since the native client rejects batches and decodes int8 as bigint. HTTP client spans are named after the bare method by default.
Effect 4.0.2 records no HTTP client headers on spans unless a header filter opts in, so the owner tracing test checks for request spans and that the secret appears in no span attribute.
…nses Bun resets a Connection: close client when the response completes before its request body is fully read, so an upstream that answers early could truncate the proxied response. The proxy now drains the remaining request body and ends the response afterwards.
…er test The 200ms timeout also covered the fsync-bound save and fired under load. A leaked lock still fails the test through the busy error once the lock retry window ends.
|
/ai-review |
There was a problem hiding this comment.
Superseded by a newer AI review
🤖 AI Review
Verified all 12 findings and merged three duplicates into nine entries: seven confirmed and two refuted. The main concerns are cancellation through the occupied PostgreSQL pool and stalled delivery of early upstream responses. Payload validation also accepts inherited property names; remaining confirmed concerns are documentation or latent robustness limitations. Full repository tests were not run.
Findings
| Severity | Location | Category | Sources | Claim |
|---|---|---|---|---|
| 🟠 MAJOR | apps/cli/src/command-internal/db-connection.pool-client.ts:203 |
cancellation |
codex | Cancellation queues behind the statement it must cancel. After five seconds, cleanup can release the still-running connection without destroying it, allowing interrupted SQL to continue executing. |
| 🟠 MAJOR | packages/stack/src/HttpProxy.ts:401 |
http-proxy |
claude+codex | Early upstream responses now wait for the entire incoming request body before the proxy ends its response. For an empty rejection, headers also remain unsent, so a streaming client waiting for that rejection can stall. |
| 🟡 MINOR | packages/stack/src/internal/reject-excess-keys.ts:27 |
input-validation |
claude+codex | The excess-key filter accepts undeclared Object.prototype property names, including constructor, toString, and proto. |
| ⚪ NIT | apps/cli/src/command-internal/db-connection.sql-pg.layer.ts:6 |
documentation |
claude | The layer comments still describe pg as @effect/sql-pg's transitive driver and describe the layer as backed by its pure-JavaScript driver, although the PR replaces that bridge with a local adapter. |
| ⚪ NIT | packages/stack/src/internal/reject-excess-keys.ts:3 |
robustness |
claude | The encoding helper converts every non-array object into a plain object, which would corrupt non-JSON encoded values such as Date or Uint8Array. Current callers do not contain such values. |
| ⚪ NIT | .agents/skills/effect/references/CONFIG.md:13 |
documentation |
claude+codex | The configuration reference only partially migrates its example: Config.String is updated, while Config.redacted and Config.boolean retain the obsolete lowercase constructor names. |
| ⚪ NIT | apps/cli/src/command-internal/db-connection.pool-client.ts:188 |
streaming |
codex | executeStream buffers the complete query result before emitting rows, rather than providing incremental database streaming. |
Refuted findings (kept for transparency, not posted as review comments)
apps/cli/src/command-internal/db-connection.pool-client.ts:32(transaction-integrity): The adapter does not pin a physical connection for transactions or reservations, so a multi-client pool could execute transaction statements on different PostgreSQL backends.
Refuted: The claimed multi-backend failure is prevented by the actual production configuration: the sole caller uses max: 1 with idleTimeoutMillis: 0. Its returned DbSession exposes ordinary execution methods, not withTransaction or reserve, and the SqlClient remains internal. The general limitation is real, but the reported transaction failure has no current call path.packages/stack/src/namespace/Namespace.integration.test.ts:605(test-coverage): Removing the 200 ms timeout weakens the interrupted-holder test by allowing a leaked lock to be released late within the busy-retry window.
Refuted: The test establishes holder completion before the second acquisition, and production cleanup releases the lock synchronously within the awaited scope finalizer. There is no later cleanup task that can release a leaked lock during retries. A leaked lock still exhausts the five-second retry window and fails the test. The removed timeout guarded elapsed runtime, rather than adding coverage of the stated leakage regression.
Stats
Claude findings: 6 · Codex findings: 6 · Confirmed: 7 · Refuted: 2 · Uncertain: 0
Models: claude-opus-5-5 + gpt-6.1-sol · Trigger: auto · Workflow run
This review runs once per PR. A maintainer can request another with a /ai-review comment.
…keys The proxy flushes upstream response headers immediately, so a client still uploading its body receives an early rejection instead of waiting for the upload to finish. Strict payloads now reject undeclared keys that exist on Object.prototype, such as constructor. Also refreshes the pg driver notes in the CLI database layer and the Config constructor names in the Effect skill.
There was a problem hiding this comment.
🤖 AI Review
Verified all nine reported findings and merged two overlapping pairs into seven entries. Confirmed the cancellation failure, early-response hang, excess-key validation gap, and stale driver documentation. Current callers limit the impact of the transaction, streaming, and nonplain-object encoding concerns; transaction severity was reduced to minor. Verification included surrounding code, trusted ADRs, and in-memory checks; full Effect runtime tests were unavailable because dependencies are not installed.
Findings
| Severity | Location | Category | Sources | Claim |
|---|---|---|---|---|
| 🟠 MAJOR | apps/cli/src/command-internal/db-connection.pool-client.ts:198 |
cancellation |
claude+codex | Interrupting a long query queues cancellation behind the same occupied single-connection pool. After five seconds, cleanup releases the still-executing connection, while the timed-out cancellation request remains queued. |
| 🟠 MAJOR | packages/stack/src/HttpProxy.ts:364 |
http-proxy |
codex | An early chunked upstream response cannot complete until the client finishes uploading its request body, so a client that stops uploading after rejection can hang. |
| 🟡 MINOR | apps/cli/src/command-internal/db-connection.pool-client.ts:32 |
transaction-isolation |
codex | The adapter does not pin a physical pool connection for SQL transactions or reservations. |
| 🟡 MINOR | apps/cli/src/command-internal/db-connection.pool-client.ts:188 |
streaming |
codex | The adapter's executeStream buffers the complete query result before emitting its first row. |
| 🟡 MINOR | packages/stack/src/internal/reject-excess-keys.ts:27 |
input-validation |
claude+codex | The excess-key guard accepts undeclared keys inherited from Object.prototype, including toString and constructor. |
| ⚪ NIT | packages/stack/src/internal/reject-excess-keys.ts:3 |
correctness |
claude | The recursive encoding helper replaces every non-array object with a plain record, corrupting nonplain encoded values if the generic wrapper is used with such schemas. |
| ⚪ NIT | apps/cli/src/command-internal/db-connection.sql-pg.layer.ts:6 |
documentation |
codex | The driver comment incorrectly describes pg as an @effect/sql-pg transitive dependency and requires alignment with a version that package no longer resolves. |
Stats
Claude findings: 3 · Codex findings: 6 · Confirmed: 7 · Refuted: 0 · Uncertain: 0
Models: claude-opus-5-5 + gpt-6.1-sol · Trigger: manual · Workflow run
This review runs once per PR. A maintainer can request another with a /ai-review comment.
A client that stops uploading after an early upstream response no longer holds the proxied response open: the proxy ends it once the body drains or after two seconds, like a lingering close.
# Conflicts: # packages/stack/src/storage/DockerDatabaseStorage.ts # packages/stack/tests/helper-owner-fixture.ts
# Conflicts: # packages/stack/src/HttpProxy.ts # packages/stack/src/Network.integration.test.ts
Bumps
effect,@effect/platform-bun,@effect/platform-node,@effect/platform-node-shared,@effect/sql-pg, and@effect/vitestfrom4.0.0-rc.112to the stable4.0.2release.@effect/tsgois unchanged.The goal is zero change to CLI behaviour. Every edit is either required by a removed or renamed API, or restores a behaviour whose upstream default moved.
Commit layout
The first three commits are the bump plus two scripted rewrites and can be skipped by reviewers:
effect/unstable/*import paths moved toeffect/*(removed upstream in rc.118, effect#8354).The remaining commits are hand migrations, one topic each.
Hand migrations
@effect/sql-pg4.0 is a native client and droppedPgClient.fromPool(effect#7426). The remote database session keeps its own node-postgres pool for the zero idle timeout, the per-connection role step-down hook, and rawCOPYconnections, sodb-connection.pool-client.tsis a smallSqlClient.makeadapter over that pool with the same statement execution, cancellation, and error classification the old bridge had.runCommandpayload schemas now reject unknown keys through aRecordfilter ahead of the struct decode, with the same "Expected no excess property" wording. Encoding dropsundefinedentries first, so optional fields passed as explicitundefined(for examplepgProve.workingDir) still cross the RPC JSON codec as they did with the plain struct.@effect/sql-pgclient sends everything through the extended protocol, which rejects multi-command strings. The realtime schema bootstrap runs as two statements, and the generatedALTER ROLE/ALTER DATABASEbatches come back from Postgres as an array and run one at a time inside the same transaction.NetAddress.SocketAddressvalues,File.seekandFile.Info.sizeusebigintandByteSize,Encodingmoved toeffect/encoding/Base64Url, and the whole-stack WebSocket test helper uses the reader and writer pair.Effect.partitionreturns[passes, fails]; thestack destroyhandler destructures in that order.packages/apicontracts are regenerated; the 4.0.2 schema codegen emits code-point length checks and Unicode-flagged patterns.SchemaAST.Union.modemoved underoptions,ToJsonSchemaOptions.additionalPropertiesbecameonExcessProperty, and regex patterns need the Unicode flag to be exported into JSON Schema (effect#8482). Peer dependency ranges are untouched and already satisfy 4.0.2.Preserved defaults
PgClient.layercalls pinidleTimeout: "10 seconds"; the default since 4.0.1 is 60 seconds (effect#8679).@supabase/configschema.jsonandproject-schema.json, mirrored atapps/docs/public/cli/*.schema.json) are byte-identical to the develop build.toCliConfigJsonSchemapassesonExcessProperty: "error"so structs keepadditionalProperties: false(effect#8147); the shared document assembler leaves pattern-keyed records open, as the generator has no per-schema setting for that; and bucket tables are modelled so they render as the published object-or-array union.Behaviour changes reviewed and accepted
These upstream changes have no configuration knob. Each was checked against the repo's call sites and judged not user-visible:
killand scoped release now wait for the process group, bounded to one second withoutforceKillAfter(effect#8018). The stack host, bundled Postgres clients, and containers already setforceKillAfter; other spawn sites can only see up to one extra second of teardown when a descendant lingers.Effect.cachedtreats interruption as abandonment (effect#8719). Every cached effect in the repo is awaited by a single fiber or forked into an owning scope, so the shared-interruption semantics do not apply.storage.analytics.bucketsandstorage.vector.bucketsare modelled as an object-or-array union instead ofSchema.Struct({}). Runtime decoding now rejects a primitive bucket value (foo = "x"), which the old schema accepted and ignored; objects and arrays decode as before.GETinstead ofhttp.client GET), following the OpenTelemetry convention. Nothing in the repo keys on the old name.Patches
@effect/platform-node-sharedstdin EPIPE listener, now against 4.0.2. 4.0.2 still removes the stdin sink's error listener before the finalend()flush, so a child that exits before reading its input raises an uncaught EPIPE on Linux; effect#7712 and effect#7714 do not cover that path.@effect/vitestrunTestpatch. Upstream's finalizer ordering fix (effect#8154) covers the abort hook, so the patch now only layers the two behaviours the stack'seffect-timeouttests assert on top of it: rejecting with the pretty errors and reporting a finalizer failure after a timeout.Test-only changes
db resete2e fixtures that seeded data throughPgClient.layerwith semicolon-joined statements now issue one statement per call; fixtures that go throughpsqlare unchanged.db resete2e fixture readscount(*)::int, since the native client decodesint8asbigint.Follow-ups
@effect/sql-pgclient and delete the pool adapter. That changes result decoding (int8, timestamps, bytea) across the SQL consumers and needs its own review.