II.
Page overview
Reference · livepage:docs-effect-baseline
Effect baseline (WP-T3-32) overview
Inspect the raw attributes, linked wiki pages, and inbound or outbound graph edges for page:docs-effect-baseline.
Attributes
nodeKind
Page
sourcePath
docs/EFFECT-BASELINE.md
sourceKind
repo-docs
title
Effect baseline (WP-T3-32)
displayName
Effect baseline (WP-T3-32)
slug
docs/effect-baseline
articlePath
wiki/docs/EFFECT-BASELINE.md
article
# Effect baseline (WP-T3-32)
Owner document for spec 10 §2 **B5** — "one pinned Effect, centrally owned by WP-T3-32; no per-package Effect versions" (`docs/specs/platform-gaps/10-t3code-migration.md`). Stage 1 (this document, landed 2026-09-28 on `phase0/hard-split-registry-guards`) puts the pin, the patch mechanism and the register in place **before any Effect code is imported** (roadmap 50 §6 risk 1). Stage 2 (apps re-platform, engine floors in CI) depends on WP-T3-01 and is listed in §7 as deferred, not done.
## 1. The pin
| Package | Version | Mechanism |
|---|---|---|
| `effect` | `4.0.0-rc.115` | root `package.json` `overrides.effect` (npm's workspace-wide pin) + root `devDependencies.effect` so the version is resolved in `package-lock.json` even while no workspace imports it |
| `@effect/vitest` | `4.0.0-rc.115` | root `overrides["@effect/vitest"]` + root `devDependencies` (pinned, installed **unpatched** — §3) |
| `patch-package` | `8.0.1` | root `devDependencies`; `postinstall: patch-package --error-on-fail` |
Root `devDependencies` is where this repository keeps shared dev tooling (`vitest`, `@vitest/coverage-v8`, docusaurus, `markdownlint-cli2`), and no `@fabricstack/*` unit exists yet to own the dependency, so the pin lives at the root rather than in a dedicated workspace (which would also have needed a spec 15 §3.3 name). When the tracked forks land (WP-T3-01…04), each declares `"effect": "4.0.0-rc.115"` verbatim; `scripts/ci/effect-baseline.test.mjs` rejects any other spec (range, caret, different version) in any workspace manifest, and any `@effect/*` package that is not an exact version.
**Pin owner:** fabric maintainers (Tal). Spec 10 assigns B5 to WP-T3-32 as a work package and names no person; the owner acts through WP-T3-32 (stage 1 here, stage 2 later) and carries the §4 rc→stable watch item into the WP-RM-02 cadence register (`docs/specs/platform-gaps/50-roadmap-workpackages.md` "WP-RM-02" AC3 lists "the Effect rc→stable watch item with its trigger criteria from `docs/EFFECT-BASELINE.md`").
**Enforced by** `npm run test:effect-baseline` (13 node:test cases, CI `test` job right after `npm ci`): one exact version in `overrides` mirrored in `devDependencies`; `npm ls effect --all --json` and `npm ls @effect/vitest --all --json` each resolve exactly one version equal to the pin; no per-package versions; the patch is applied (§2) and `@effect/vitest` is unpatched (§3); the inventory and this document carry what the spec requires.
## 2. Patch provenance
| | |
|---|---|
| Upstream project | T3 Code (t3code), **T3 Tools Inc., MIT** (intent D15). Attribution: `third_party/t3code-patches/LICENSE` + `PATCHES.md`. |
| Snapshot | commit `de251fc2971a884cb5b1305ba4daf309dc8cccb0` (read-only reference; never imported wholesale) |
| Upstream file | `patches/effect@4.0.0-rc.115.patch`, wired by `pnpm-workspace.yaml` `patchedDependencies` `effect@4.0.0-rc.115` |
| Fabric file | `patches/effect+4.0.0-rc.115.patch` (patch-package naming) |
| Port | path prefixes only — `a/<file>` → `a/node_modules/effect/<file>` on the `diff --git`, `---`, `+++` lines; hunk bodies byte-identical to upstream (verified by `diff` of the non-header lines at port time) |
| Applied by | `postinstall: patch-package --error-on-fail` — a patch that no longer applies fails the install instead of being skipped |
What the patch changes (5 files, 23 hunks) and how each is pinned by the regression test:
| Area | Hunks | Behaviour | Regression assertion |
|---|---|---|---|
| `dist/unstable/rpc/RpcClient.js` + `.d.ts` | 17 + 2 | New `RequestHooks` service (`effect/rpc/RpcClient/RequestHooks`) with `onRequestStart/onRequestChunk/onRequestExit/onRequestInterrupt`, threaded through `makeNoSerialization`; `ConnectionHooks` gains `onPing/onPong/onPingTimeout`; the socket pinger tolerates three missed pongs before it times out and `onPong` becomes an Effect | `import('effect/unstable/rpc/RpcClient')` → `RequestHooks` is a service class with that key, and the `.d.ts` declares it (public surface). The pinger internals are pinned on the patched source text (fallback, marked in the test). |
| `dist/unstable/http/HttpClientResponse.js` + `src/…/HttpClientResponse.ts` | 1 + 1 | `cookies` reads `headers.getSetCookie?.() ?? []` so a response whose `headers` is not a web `Headers` no longer throws | `HttpClientResponse.fromWeb(req, { status, headers: {…} }).cookies` is empty instead of a `TypeError`; a real `Response` still parses `Set-Cookie` (control). |
| `dist/unstable/ai/McpServer.js` | 2 | HTTP `DELETE <path>` ends the MCP session (400 without the session header, 404 unknown, 204 deleted) instead of the 405 the stock `layerHttp` returned | Pinned on the patched source text (fallback: needs an HttpRouter + RpcServer protocol stack to exercise). |
The patch file is additionally parsed with patch-package's own parser (5 files, the hunk counts above) and re-applied with `--error-on-fail` inside the test, so a corrupted or pnpm-named file is caught, not ignored.
The 22 other upstream patches are inventoried with a decision each in `docs/specs/platform-gaps/phase0/t3code-patches-inventory.md` (1 ported, 1 dropped as toolchain-only, **17** dropped as mobile-only — not the 19 the WP brief assumed, because `@legendapp/list` and `@pierre/diffs` are also consumed by `t3:apps/web` — and 4 deferred to the WPs that land their consumers).
## 3. `@effect/vitest`: pinned, deliberately unpatched
The upstream `@effect__vitest@4.0.0-rc.115.patch` has exactly one effect: every `import … from "vitest"` / `export * from "vitest"` in `dist/` and `src/` becomes `vite-plus/test`, so the package runs under Voidzero's private vite-plus test runner. Spec 10 §2 excludes vite-plus and WP-T3-32 stage 2 re-platforms on stock vitest, under which the *unpatched* package's `vitest` imports are exactly what is wanted. Porting the patch would make the package import a module fabric does not install. Decision: **DROPPED-toolchain**; the test asserts the installed `dist/index.js`, `dist/internal/internal.js` and `dist/utils.js` import `vitest` and never `vite-plus`, and that no `@effect+vitest*.patch` exists.
**Stage-2 input, recorded here so it is not a surprise:** `@effect/vitest@4.0.0-rc.115` declares `peerDependencies.vitest: ">=5.0.0 <6.0.0"` while fabric pins `vitest 4.1.6` (root `devDependencies`). The pin is therefore resolved and locked today (installs pass under `--legacy-peer-deps`, which CI already uses) but the package is **not yet usable as a test harness in fabric** — the vitest major bump is part of the stage-2 Vite/vitest re-platform decision, together with the `vite`/`@vitejs/plugin-react` lines the apps will need.
## 4. Unstable-namespace usage register
Research R2 §15 / §18.12 item 3: adopting the server, contracts and the protocol forks means adopting `effect/unstable/{rpc,sql,process,cli,http}` — namespaces the Effect team may still rename or reshape before 4.0.0 stable. Counts are non-test-excluded `*.ts`/`*.tsx` files importing the namespace in the t3code snapshot (`grep -rl`), by workspace; "fabric unit" is the spec 15 §3.3 name the code lands under.
| Namespace | t3code usage at the snapshot (files) | Fabric units that will use it (landing WP) | Used in fabric today | Owner | Transition criterion (rc→stable) |
|---|---|---|---|---|---|
| `effect/unstable/rpc` | server 7, effect-acp 6, client-runtime 6, contracts 1, relay 1, mobile 1 | `@fabricstack/contracts` (WP-T3-02), `@fabricstack/acp` (WP-T3-03), `@fabricstack/codex-protocol` (WP-T3-04), `@fabricstack/client-runtime` (WP-T3-24), `@fabricstack/environment` (WP-T3-19), `@fabricstack/relay` (WP-T3-31) | none | fabric maintainers (Tal) | `Rpc`/`RpcGroup`/`RpcClient`/`RpcServer` published outside `unstable/` (or documented as final), and the `RequestHooks` behaviour of §2 either upstream or re-ported; the 144-`Rpc.make` contracts surface (R2 §18.12 item 4) compiles unchanged or with a mechanical codemod |
| `effect/unstable/sql` | server 118, desktop 6, shared 2, relay 1 | `@fabricstack/environment` (WP-T3-19: the `node:sqlite` Effect SQL client, `t3:packages/shared/src/nodeSqliteClient.ts`), `@fabricstack/relay` (WP-T3-31: `@effect/sql-pg`) | none | fabric maintainers (Tal) | `SqlClient`/`SqlSchema`/migrator stable; the event-sourced persistence (decider/projector commit in one transaction — R2 §18.12 item 5) replays a captured event log unchanged across the bump |
| `effect/unstable/process` | server 97, desktop 23, codex-app-server 6, effect-acp 6, ssh 5, tailscale 2, shared 2, scripts 19 | `@fabricstack/environment` (WP-T3-19; provider spawns for the ACP/Codex lanes WP-T3-06…13), `@fabricstack/acp` / `@fabricstack/codex-protocol` (WP-T3-03/04 client spawners), `@fabricstack/ssh` (WP-T3-28), `@fabricstack/tailscale` (WP-T3-29), `@fabricstack/console-desktop` (WP-T3-26) | none | fabric maintainers (Tal) | `ChildProcessSpawner`/`ChildProcess` stable with the same spawn/stdio/kill semantics; the ACP mock-agent protocol tests (spec 30 Q-07) green |
| `effect/unstable/cli` | server 26, effect-acp 1, scripts 16 | `@fabricstack/environment` (WP-T3-19: the `environment-server` bin is an `effect/unstable/cli` Command tree, `t3:apps/server/src/bin.ts`); build scripts if adopted by spec 40 | none | fabric maintainers (Tal) | `Command`/`Flag`/`Argument` stable; the `environment-server` subcommand tree and `--help` output golden-tested before and after |
| `effect/unstable/http` | server 94, relay 14, client-runtime 13, desktop 13, web 10, shared 6, contracts 5, mobile 5, ssh 2, tailscale 1, codex-app-server 1, effect-acp 1, scripts 5 | `@fabricstack/environment` (WP-T3-19: HTTP + WS via Effect platform, `t3:apps/server/src/{http,ws}.ts`), `@fabricstack/relay` (WP-T3-31), `@fabricstack/client-runtime` (WP-T3-24), `@fabricstack/contracts` (WP-T3-02: `environmentHttp.ts` HttpApi groups), `@fabricstack/console-web`/`console-desktop` (WP-T3-25/26), `@fabricstack/ssh`/`tailscale` (WP-T3-28/29) | none | fabric maintainers (Tal) | `HttpClient`/`HttpRouter`/`HttpServer`/`HttpApi` stable; the `getSetCookie` fallback of §2 upstream or unnecessary; DPoP/pairing auth (WP-T3-30) conformance suite green |
| Watch: other `unstable/*` seen in the snapshot | `reactivity` 218, `httpapi` 36, `observability` 14, `socket` 11, `ai` 9 (the MCP server patch of §2 lives here), `persistence` 6, `net` 4, `encoding` 3, `arbitrary` 1 | added to this table when the landing WP first imports them | none | fabric maintainers (Tal) | same rule: no namespace is imported by a fabric unit before it has a row here |
**rc→stable watch item** (standing WP-RM-02 cadence-register entry; owner as above). The pin moves from `4.0.0-rc.115` to a stable `4.x` only when all of the following hold, and only via the §6 playbook:
1. `npm view effect dist-tags.latest` is a non-prerelease `4.x`, and its changelog covers every `unstable/*` namespace in the table (graduated, renamed with a codemod, or explicitly kept unstable with a stability promise).
2. The three patched behaviours of §2 are either in the stable release, re-ported as a new `patches/effect+<version>.patch` with the regression test updated, or dropped with a written reason in the inventory.
3. `@effect/vitest` has a matching stable release whose `vitest` peer range includes the vitest fabric runs after stage 2.
4. Every tracked fork (`contracts`, `acp`, `codex-protocol`, `ssh`, `tailscale`) and `apps/environment` compiles and tests on the candidate (the stage-2 build matrix, AC3).
5. `npm run test:effect-baseline` is green on the candidate (singleton, no per-package versions, patch state as decided).
Until then the pin is exact everywhere: no `^`, `~` or `latest` for `effect` or `@effect/*` in any manifest (the test rejects them).
## 5. Node floors (recorded now as stage-2 input; not enforced by this stage)
Quoted from spec 10 "WP-T3-32" and research R2 §15 so stage 2 encodes them without re-deriving:
- Workspace engines **≥22.16** — "workspace engines ≥22.16 (environment server `^22.16 || ^23.11 || >=24.10` — R2 §15)"; the environment server's own `engines` range is `^22.16 || ^23.11 || >=24.10` (`t3:apps/server/package.json`).
- `node:sqlite` **≥22.5** — "`node:sqlite` dependency (Node 22.5+)" (R2 §15); fabric already runs the gateway on a `22.13.0` floor for the same module (`.github/workflows/ci.yml` `gateway-node-engine`, FIX-008).
- SEA build hosts **≥25.7** — "SEA build requires Node 25.7+ host" (R2 §15, `t3:apps/server/scripts/cli.ts:160`); consumed by spec 40 P-03 / WP-PKG-03 AC6, never by the workspace engines floor (spec 40 §1.4 keeps the two apart).
- Today: the root manifest declares no `engines`; workspace floors range from `>=18.0.0` (`packages/adapters/extensions`) to `>=22.13.0` (`adapters-cli`, `adapters-gateway`); CI runs Node 22 with a `22.13.0` matrix leg. Raising the workspace floor to ≥22.16 and adding the sub-floor negative run (AC5) is stage 2.
## 6. Upgrade playbook
1. **Decide** under §4 (rc→rc bumps need only items 2 and 5; rc→stable needs all five). Record the decision in a new section of `docs/specs/platform-gaps/phase0/effect-baseline.md` (or its successor record).
2. **Move the pin in one place per package**: root `package.json` `overrides.effect` and `devDependencies.effect` (same string), likewise `@effect/vitest`; every workspace manifest that declares `effect` (verbatim copies of the same string — the test enforces it).
3. **Re-port the patch**: `git mv patches/effect+<old>.patch patches/effect+<new>.patch`; `npm install --legacy-peer-deps` (CI's flags); if `patch-package --error-on-fail` rejects a hunk, open `node_modules/effect/...` at the rejected site, decide per hunk (upstream already has it → drop the hunk; drifted → re-apply by hand), then regenerate with `npx patch-package effect` and diff the result against the old file hunk by hunk. Never delete `package-lock.json` to "fix" an install.
4. **Update the regression test** for any hunk whose behaviour changed (`scripts/ci/effect-baseline.test.mjs`: `EXPECTED_PATCH_FILES` hunk counts and the behaviour assertions) and the tables in §2 and `third_party/t3code-patches/PATCHES.md`.
5. **Prove**: `npm run test:effect-baseline`; `npm ls effect --all` shows one version; the stage-2 build matrix (tracked forks + `apps/environment`) once it exists; `node scripts/ci/publish-isolation-guard.cjs --self-test` and `npm run guard:packages` unaffected.
6. **Refresh the inventory** if the t3code snapshot moved (`t3code-patches-inventory.md`: new upstream patch list, decisions re-checked) and the attribution record's snapshot SHA.
7. **Commit** with the pin in the subject (`build(effect): bump pin to effect@<version>, re-port patch`) and push; the CI `test` job runs the baseline after `npm ci`.
## 7. Stage 2 — explicitly deferred (depends on WP-T3-01)
Not done in stage 1, per spec 10 "WP-T3-32" and the roadmap (M2 exit criterion 7; risk rows 2–3):
- **Apps re-platform**: stock Vite + vitest + fabric's `tsc` replace vite-plus (`vite: npm:@voidzero-dev/vite-plus-core`) and TS7/tsgo in `apps/console-web`, `apps/console-desktop`, `apps/environment` build scripts; grep gate for zero `@voidzero-dev/*` in the dependency graph (AC4). Includes the vitest major decision that unblocks `@effect/vitest` (§3).
- **Engine floors in CI**: root and workspace `engines` ≥22.16, a sub-floor Node run that fails the engines check (AC5), the Node matrix in `ci.yml`/`live-stack.yml`, SEA host floor recorded for WP-PKG-03.
- **Build matrix**: all five tracked forks + `apps/environment` compile and test under stage-1 tooling (AC3) — impossible until WP-T3-01 imports them.
- **AC1–2, AC6** are the stage-1 acceptance criteria and are what this document and `npm run test:effect-baseline` deliver.
## 8. Files
| Path | Role |
|---|---|
| `package.json` (root) | `overrides`, `devDependencies`, `postinstall`, `test:effect-baseline` |
| `patches/effect+4.0.0-rc.115.patch`, `patches/README.md` | the ported patch and the directory's rules |
| `scripts/ci/effect-baseline.test.mjs` | singleton, per-package-version, patch-regression, patch-file, `@effect/vitest`, inventory, doc and CI-wiring tests |
| `.github/workflows/ci.yml` | `test` job step "Effect baseline (single pin, patch regression, inventory)" |
| `docs/specs/platform-gaps/phase0/t3code-patches-inventory.md` | the 23 upstream patches with decisions |
| `docs/specs/platform-gaps/phase0/effect-baseline.md` | execution record of stage 1 |
| `third_party/t3code-patches/` | MIT attribution and provenance (D15) |
documents
[]
Outgoing edges
None.
Incoming edges
contains_page1
- page:docs·PageBabysitter Docs