From 4d4f345e9a11195598801df18ad2c9b950da752e Mon Sep 17 00:00:00 2001 From: BL Date: Tue, 13 Oct 2026 15:21:41 +0000 Subject: [PATCH 1/2] Move to stable Effect 4.0.x and release as 0.2.0 Peer effect becomes ">=4.0.3 <4.1" (was rc.112 || rc.115), the dev dependency is pinned to 4.0.4, and the packed-consumer matrix runs Effect 4.0.3 and 4.0.4. No source file changed: the built dist is byte-identical to 0.1.1. Adds CHANGELOG.md with the behaviour audit and the consumer migration note, and ships it in the package. Co-Authored-By: Claude Opus 5.5 --- .github/RELEASING.md | 2 +- CHANGELOG.md | 67 ++++++++++++++++++++++++++++++++++++++++++ README.md | 12 +++++--- bun.lock | 4 +-- package.json | 7 +++-- scripts/pack-check.mjs | 3 +- 6 files changed, 84 insertions(+), 11 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.github/RELEASING.md b/.github/RELEASING.md index 514e035..52d4387 100644 --- a/.github/RELEASING.md +++ b/.github/RELEASING.md @@ -86,5 +86,5 @@ The existing consumer script accepts these optional environment variables: A registry check uses `CODEC_PACK_SOURCE=@misofm/codec@` and the expected hash from the release qualification. It checks the actual registry tarball in -isolated installations across Effect rc.112/rc.115, Bun, Node, Chromium, Firefox, +isolated installations across Effect 4.0.3/4.0.4, Bun, Node, Chromium, Firefox, and WebKit. Keep receipts in the issue evidence; do not commit the tarball. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..eae1313 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,67 @@ +# Changelog + +All notable changes to `@misofm/codec`, newest first. + +## 0.2.0 + +Moves the package from Effect release candidates (`4.0.0-rc.112 || 4.0.0-rc.115`) +to stable Effect `4.0.x`: developed and tested on `4.0.4`, peer floor `4.0.3`. +The public API, the Wasm assets, and the built JavaScript are unchanged. The +`dist/` output compiled against Effect 4.0.4 is byte-identical to the 0.1.1 output +(every `.js`, `.d.ts`, and map), and no source file changed. + +### Changed (breaking) + +- **Peer `effect` is now `>=4.0.3 <4.1`** (was `4.0.0-rc.112 || 4.0.0-rc.115`). + Release candidates are no longer supported; stay on 0.1.x for them. + +### Behaviour changes from Effect 4.0.x + +The codec uses `Stream.transformPullBracket`, `Stream.rechunk`, `Stream.runForEach`, +`Pull.matchEffect`, `Cause.done`, `Scope.addFinalizer`, `Effect.acquireRelease`, +`Effect.scoped`, `Effect.try`/`tryPromise`, `Effect.forEach`, `Effect.yieldNow`, +`Schema.decodeUnknownEffect`, `Schema.TaggedError`, `Schema.Defect`, `Layer.effect`, +and `Context.Service`. It uses no `partition`, `Effect.retry`, `Schedule`, +`Effect.timeout`, `Worker`, or `Effect.async`/`callback`, so the partition-order, +retry, schedule, and TimeoutError-text changes do not apply. + +A differential run of the same script against the 0.1.1 package on `effect@4.0.0-rc.115` +and the 0.2.0 package on `effect@4.0.4` (also 0.1.1 on 4.0.4 and 0.2.0 on rc.115) +produced identical output for: + +- the `message`, `name`, JSON, `Cause.pretty`, and Schema-encoded forms of + `FlacDecodeError`, `FlacEncodeError`, and `PcmInputError` (with and without + `cause`, and with `bigint` fields); +- the encoded FLAC bytes, STREAMINFO MD5, decoded PCM bytes, and `Complete` event; +- the exit reasons for truncated input, a failing source (the source's error object + is still returned by identity), invalid options, and invalid PCM blocks; +- interruption while the decoder waits for input (one `Interrupt` reason, source + finalizer run once), early `Stream.take` (no `Complete`, source finalizer run once), + `Effect.timeout` on a stalled source, and interruption of an encode with a pending + source (one `Interrupt` reason, no late sink writes). + +Effect 4.0.x changes that touch this code, and why they are unobservable here: + +- `Scope.close` finalizers run uninterruptibly (Effect rc.118). The decoder release + finalizer and the encoder `acquireRelease` release are synchronous and bounded, so + interruption could not abandon them before either. +- A pending interruption is kept when an uninterruptible effect fails, and typed + failures are dropped when interruption skips recovery handlers (Effect 4.0.1, #8652). The Wasm calls are + synchronous and the codec has no uninterruptible region of its own, so a decode or + encode is still interrupted at the next pull, input wait, or sink write. +- `Stream.rechunk` no longer fails on large source chunks (Effect rc.113, #7655). The decoder + rechunks to single elements and rejects chunks over `maxInputChunkBytes` itself. + +### Migrating from 0.1.x + +1. Move your whole Effect release train to stable together and pin it to one `4.0.x` + version, for example `"effect": "4.0.4"` plus every `@effect/*` package you use at + `4.0.4`. A mixed release-candidate and stable install fails to resolve `effect/*` + module paths, and two copies of `effect` give the shared `Context.Service` tags, + tagged errors, and `Layer` internals different identities. In particular, a + Layer built by one copy's runtime and constructed by another copy fails with + `Cannot read properties of undefined (reading 'scheduler')`: this happens when a + dependency such as `@misofm/engine-web-adapter@0.5.x` still pins + `effect@4.0.0-rc.112` while the app and `@misofm/codec` use 4.0.x. Upgrade every + `@misofm/*` package that depends on `effect` in the same change. +2. Nothing else changes in code that imports `@misofm/codec`. diff --git a/README.md b/README.md index 0129fce..c929652 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,16 @@ files, media pipelines, delivery chunks, manifests, storage, and publication. See [package boundaries](docs/architecture.md) and the [engine web adapter integration contract](docs/adapter-integration.md). -Install the package with a supported Effect peer: +Install the package with a supported stable Effect peer (`>=4.0.3 <4.1`), and +pin the whole Effect release train you use to one exact version: ```sh -bun add @misofm/codec effect@4.0.0-rc.115 +bun add @misofm/codec effect@4.0.4 ``` +See the [changelog](CHANGELOG.md) for the migration from 0.1.x (Effect release +candidates). + ## Encoding `encodeFlac(source, sink, options)` accepts an Effect stream of signed, @@ -138,8 +142,8 @@ repository and uses the resulting CLI only as a test oracle: CODEC_NATIVE_FLAC=$(scripts/build-native-flac.sh) bun run test:integration ``` -The supported peer matrix is `effect@4.0.0-rc.112` and `4.0.0-rc.115`; development -pins rc.115. Packed consumer checks execute real incremental encoding, decoding, +The supported peer range is `effect >=4.0.3 <4.1`; the packed-consumer matrix +runs `effect@4.0.3` and `4.0.4`, and development pins `4.0.4`. Packed consumer checks execute real incremental encoding, decoding, round trips, cancellation, and typed truncation failures on Bun 1.4.2, Node 22.23.2, and Chromium, Firefox, and WebKit workers. Consumer examples are compiled from an isolated installed tarball, without workspace aliases. diff --git a/bun.lock b/bun.lock index 0926a2d..e621455 100644 --- a/bun.lock +++ b/bun.lock @@ -7,7 +7,7 @@ "devDependencies": { "@types/bun": "1.3.9", "@types/node": "22.20.2", - "effect": "4.0.0-rc.115", + "effect": "4.0.4", "playwright": "1.62.1", "prettier": "3.6.2", "typescript": "5.9.3", @@ -24,7 +24,7 @@ "bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="], - "effect": ["effect@4.0.0-rc.115", "", {}, "sha512-ogYulZ5ffeOzrJqQrG0XOkDO5lKn2s9KNHhoxJy/6wKUkF0i12dlVXWA9TgsgQ+zV/JzizyG0+XMPepZEQX6vw=="], + "effect": ["effect@4.0.4", "", {}, "sha512-Sr50OeVSC3M5UkoUYUhFf3EULryhGaZZUf6OzDHTvaznOw7HRlxOKdF3/4nvQXHL22StOyMG3Cvo8Bt7xUp+Ng=="], "fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], diff --git a/package.json b/package.json index b159eb2..4baa41f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@misofm/codec", - "version": "0.1.1", + "version": "0.2.0", "description": "Effect-based audio codec primitives backed by pinned libFLAC WebAssembly", "license": "Apache-2.0", "repository": { @@ -35,6 +35,7 @@ "wasm/*.wasm", "wasm/*.manifest.json", "README.md", + "CHANGELOG.md", "docs/*.md", "LICENSE", "THIRD_PARTY_NOTICES.md", @@ -54,12 +55,12 @@ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p benchmark/tsconfig.json --noEmit" }, "peerDependencies": { - "effect": "4.0.0-rc.112 || 4.0.0-rc.115" + "effect": ">=4.0.3 <4.1" }, "devDependencies": { "@types/bun": "1.3.9", "@types/node": "22.20.2", - "effect": "4.0.0-rc.115", + "effect": "4.0.4", "playwright": "1.62.1", "prettier": "3.6.2", "typescript": "5.9.3" diff --git a/scripts/pack-check.mjs b/scripts/pack-check.mjs index c296157..60403dc 100644 --- a/scripts/pack-check.mjs +++ b/scripts/pack-check.mjs @@ -19,7 +19,7 @@ import { fileURLToPath } from "node:url"; const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const scratch = await mkdtemp(join(tmpdir(), "misofm-codec-packed-")); -const peers = ["4.0.0-rc.112", "4.0.0-rc.115"]; +const peers = ["4.0.3", "4.0.4"]; const receipts = []; const packSource = process.env.CODEC_PACK_SOURCE; const expectedSha256 = process.env.CODEC_PACK_EXPECTED_SHA256; @@ -117,6 +117,7 @@ try { "wasm/flac-encoder.wasm", "wasm/flac-decoder.wasm", "THIRD_PARTY_NOTICES.md", + "CHANGELOG.md", "docs/architecture.md", "docs/adapter-integration.md", "vendor/licenses/libFLAC.txt", From aaa5185a4b26c684bc6646bf88ad833343058827 Mon Sep 17 00:00:00 2001 From: BL Date: Tue, 13 Oct 2026 16:05:25 +0000 Subject: [PATCH 2/2] Regenerate bun.lock peers, correct the CHANGELOG, add spec 010 bun.lock recorded the workspace peer as the old rc range; regenerated. CHANGELOG: the identical-output claim excluded a SchemaError cause (JSON is now compact; the path is an ABI violation the pinned asset does not produce), and the rechunk note is dropped because the decoder's rechunk(1) never took that path. Adds the numbered spec for issue #10 as AGENTS.md requires. Co-Authored-By: Claude Opus 5.5 --- ...0-move-the-codec-to-stable-effect-4-0-x.md | 29 +++++++++++++++++++ CHANGELOG.md | 20 ++++++++----- bun.lock | 2 +- 3 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 .github/ISSUE_SPECS/010-move-the-codec-to-stable-effect-4-0-x.md diff --git a/.github/ISSUE_SPECS/010-move-the-codec-to-stable-effect-4-0-x.md b/.github/ISSUE_SPECS/010-move-the-codec-to-stable-effect-4-0-x.md new file mode 100644 index 0000000..66a3980 --- /dev/null +++ b/.github/ISSUE_SPECS/010-move-the-codec-to-stable-effect-4-0-x.md @@ -0,0 +1,29 @@ +# Move the codec to stable Effect 4.0.x (0.2.0) + +GitHub: https://github.com/misofm/codec/issues/10 + +## Objective and immutable scope + +Move `@misofm/codec` from Effect release candidates (peer `4.0.0-rc.112 || 4.0.0-rc.115`) to stable Effect `4.0.x` so `misofm/app` and `@misofm/engine-web-adapter` can share one Effect 4.0.4 copy. Release as 0.2.0. Preserve the public API (built `dist` identical to 0.1.1), the Wasm assets (encoder `90da75c7…`, decoder `70caf381…`), bounds, cancellation, and backpressure. No source, ABI, or asset change; no adapter, app, or Engine work. + +Why: `engine-web-adapter@0.5.13` depended on `effect@4.0.0-rc.112`. When the application shares one Effect 4.0.x copy with the codec (deduplicated install, for example bun's loose peers), the adapter's rc.112 runtime runs a codec `Layer` built by that copy and fails with `Cannot read properties of undefined (reading 'scheduler')`. Unit tests did not catch it because they resolve one Effect copy. + +Base: origin/main `10abab98191b878482a02c0d8e9a2572c8eb0958` (codec 0.1.1). Branch `effect-4.0.4`, PR https://github.com/misofm/codec/pull/9. Effect guidance: [skills revision 0507930](https://github.com/unconfirmedlabs/skills/tree/0507930b6dd13f39739aba40b86a6afb63d32e09/effect-ts) (`references/migration.md`, "Effect v4 release candidates to 4.0.x"). + +## Implementation contract + +- `package.json`: version 0.2.0, peer `effect >=4.0.3 <4.1`, dev dependency `effect` pinned exactly to 4.0.4 (the codec imports only `effect`; there is no `@effect/*` package), `CHANGELOG.md` in `files`. Regenerate `bun.lock`. +- `scripts/pack-check.mjs` runs the isolated packed-consumer matrix on Effect 4.0.3 and 4.0.4 (Bun, Node, Chromium, Firefox, WebKit) and requires `CHANGELOG.md` in the tarball. +- `CHANGELOG.md` records the behaviour audit and the consumer migration note. README and `.github/RELEASING.md` name the new matrix. +- Historical evidence (`docs/evidence/packed-consumers.json`, `.github/RELEASE_EVIDENCE/003`, `006`) is not rewritten. + +## Evidence (candidate, not published) + +- Compiles unchanged against 4.0.4; the built `.js`, `.d.ts`, and maps are byte-identical to the 0.1.1 output. +- Differential script across {0.1.1, 0.2.0} × {rc.115, 4.0.4}: identical error shapes (message, JSON, `Cause.pretty`, Schema encoding), encoded FLAC and decoded PCM hashes, exit reasons, source-error identity, interruption while waiting for input, early take, timeout on a stalled source, and encode interruption without late writes. One measured exception: `JSON.stringify` of a `FlacDecodeError` whose `cause` is a `SchemaError` is now compact (Effect #8895); the only producer is an ABI-violation path (see `CHANGELOG.md`). +- Local: `bun run check` (40 pass, 5 native-only skips), pinned-native `bun run test:integration` (5 pass), `bun run build:wasm:verify` (both assets reproduce), `bun run pack:check` on 4.0.3 and 4.0.4. `npm install --strict-peer-deps` plus `npm ls` valid on 4.0.4 and 4.0.3; `4.0.0-rc.115` is rejected with `ERESOLVE`. +- Repro of the original failure and its absence with codec 0.2.0 plus adapter 0.6.0: see the adapter PR https://github.com/misofm/engine-web-adapter/pull/130. + +## Release contract and blockers + +Release follows `.github/RELEASING.md`: merge a reviewed, green PR; run `npm-publish.yml` on `main` with `mode=publish`, the exact 40-character merge SHA, and `expected_version=0.2.0`; create the `v0.2.0` release/tag at that commit; record the run URL, registry integrity, and decoder hash here and in `.github/RELEASE_EVIDENCE/010/registry-release.json`. Blockers: the adapter PR installs this codec from the throwaway branch `effect-4.0.4-ci-dist` and must be flipped to the published 0.2.0 after release; delete that branch afterwards. Publication has not been performed. diff --git a/CHANGELOG.md b/CHANGELOG.md index eae1313..f3c4aef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,8 +30,8 @@ and the 0.2.0 package on `effect@4.0.4` (also 0.1.1 on 4.0.4 and 0.2.0 on rc.115 produced identical output for: - the `message`, `name`, JSON, `Cause.pretty`, and Schema-encoded forms of - `FlacDecodeError`, `FlacEncodeError`, and `PcmInputError` (with and without - `cause`, and with `bigint` fields); + `FlacDecodeError`, `FlacEncodeError`, and `PcmInputError` (with and without a plain + `Error` as `cause`, and with `bigint` fields); - the encoded FLAC bytes, STREAMINFO MD5, decoded PCM bytes, and `Complete` event; - the exit reasons for truncated input, a failing source (the source's error object is still returned by identity), invalid options, and invalid PCM blocks; @@ -40,17 +40,23 @@ produced identical output for: `Effect.timeout` on a stalled source, and interruption of an encode with a pending source (one `Interrupt` reason, no late sink writes). +One difference is known and was measured: `JSON.stringify` of a `FlacDecodeError` whose +`cause` is an Effect `SchemaError` now yields the compact `{ "_tag": "SchemaError", +"message": ... }` object (Effect 4.0.3, #8895) instead of a dump of the schema AST. Its +Schema-encoded form and `Cause.pretty` output are unchanged. The codec sets such a cause +in one place only: when the decoder Wasm reports a PCM format that `PcmFormat` rejects +(`reason: "wasm-abi"`), an ABI violation that the pinned decoder asset does not produce, +so the path is effectively unreachable. + Effect 4.0.x changes that touch this code, and why they are unobservable here: - `Scope.close` finalizers run uninterruptibly (Effect rc.118). The decoder release finalizer and the encoder `acquireRelease` release are synchronous and bounded, so interruption could not abandon them before either. - A pending interruption is kept when an uninterruptible effect fails, and typed - failures are dropped when interruption skips recovery handlers (Effect 4.0.1, #8652). The Wasm calls are - synchronous and the codec has no uninterruptible region of its own, so a decode or - encode is still interrupted at the next pull, input wait, or sink write. -- `Stream.rechunk` no longer fails on large source chunks (Effect rc.113, #7655). The decoder - rechunks to single elements and rejects chunks over `maxInputChunkBytes` itself. + failures are dropped when interruption skips recovery handlers (Effect 4.0.1, #8652). + The Wasm calls are synchronous and the codec has no uninterruptible region of its own, + so a decode or encode is still interrupted at the next pull, input wait, or sink write. ### Migrating from 0.1.x diff --git a/bun.lock b/bun.lock index e621455..51d87d7 100644 --- a/bun.lock +++ b/bun.lock @@ -13,7 +13,7 @@ "typescript": "5.9.3", }, "peerDependencies": { - "effect": "4.0.0-rc.112 || 4.0.0-rc.115", + "effect": ">=4.0.3 <4.1", }, }, },