Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
1a2b66f
chore: checkpoint Effect 4 cohort flip
schickling-assistant Jul 28, 2026
ff8cf99
chore: mark Effect 4 platform patch bridge
schickling-assistant Jul 28, 2026
f67e194
chore: checkpoint effect 4 bootstrap graph
schickling-assistant Jul 29, 2026
8f10e27
chore: bootstrap Effect 4 dependency graph
schickling-assistant Jul 29, 2026
8e40ac0
chore: pin pre-flip ci-tools migration bridge
schickling-assistant Jul 29, 2026
001fbfb
chore: complete bounded pre-flip toolchain bridge
schickling-assistant Jul 29, 2026
d439040
docs: add Effect 4 atom migration recipe
schickling-assistant Jul 29, 2026
f59c566
wip: apply Effect 4 atom recipe
schickling-assistant Jul 29, 2026
a61f8a0
wip: start Effect 4 otel root import repair
schickling-assistant Jul 29, 2026
2e95c74
fix: repair otel contract Effect 4 root imports
schickling-assistant Jul 29, 2026
6bbd0b0
wip: checkpoint Effect 4 logger root import cleanup
schickling-assistant Jul 29, 2026
9ef539a
fix: repair genie root imports for Effect 4
schickling-assistant Jul 29, 2026
f21d039
fix: update genie process service type for Effect 4
schickling-assistant Jul 29, 2026
4458202
fix: repair Effect 4 import and schema load blockers
schickling-assistant Jul 29, 2026
3b5b743
fix: repair Effect 4 schema and context loader APIs
schickling-assistant Jul 29, 2026
b9e1af3
fix: repair Effect 4 logger union and annotation APIs
schickling-assistant Jul 29, 2026
5e17ab8
docs: record Effect 4 loader mapping handoff
schickling-assistant Jul 29, 2026
ea6c6f6
fix: complete Effect 4 platform import sweep
schickling-assistant Jul 29, 2026
671e01f
fix: bootstrap Effect atom react manifests
schickling-assistant Jul 29, 2026
0b029d4
fix: update atom react lockfile
schickling-assistant Jul 29, 2026
dd5d397
docs: fix SchemaIssue Forbidden recipe
schickling-assistant Jul 29, 2026
57cef25
fix: use Effect 4 Cause error lookup
schickling-assistant Jul 29, 2026
e53bc72
fix: accept Effect 4 schema AST tags
schickling-assistant Jul 29, 2026
4aeabdc
fix: read Effect 4 schema representations
schickling-assistant Jul 29, 2026
d278d22
fix: guard legacy schema annotation fallback
schickling-assistant Jul 29, 2026
3152e66
fix: use Effect 4 optional Option schema
schickling-assistant Jul 29, 2026
8674a46
fix: apply Effect 4 optional schema constructors
schickling-assistant Jul 29, 2026
3c24b80
fix: use Effect 4 context services
schickling-assistant Jul 29, 2026
b91e882
fix: use Effect 4 TUI layers
schickling-assistant Jul 29, 2026
649e613
fix: use Effect 4 catch handler
schickling-assistant Jul 29, 2026
ab4fcba
fix: use Effect 4 cause handler
schickling-assistant Jul 29, 2026
69de224
fix: use Effect 4 interrupt cause predicate
schickling-assistant Jul 29, 2026
b2f1b2e
fix: use Effect 4 CLI runWith
schickling-assistant Jul 29, 2026
df87dd3
fix: pass user args to Effect 4 CLI
schickling-assistant Jul 29, 2026
95f391d
fix: use Effect 4 schema encode result
schickling-assistant Jul 29, 2026
995143f
fix: use namespace platform service imports
schickling-assistant Jul 29, 2026
836a3de
fix: use Effect 4 direct runtime runners
schickling-assistant Jul 29, 2026
69ca2f5
fix: use Effect 4 schema constraints for TUI output
schickling-assistant Jul 29, 2026
59a48d0
fix: use Effect 4 literal unions in genie state
schickling-assistant Jul 29, 2026
227bea6
fix: use Effect 4 schema encoders in TUI JSON output
schickling-assistant Jul 29, 2026
c64a735
fix: use Effect 4 PubSub subscription APIs
schickling-assistant Jul 29, 2026
1fbdeed
fix: use Effect 4 defect handler in genie
schickling-assistant Jul 29, 2026
04d8f7c
fix: use Effect 4 result API in genie
schickling-assistant Jul 29, 2026
dec93d5
fix: use Effect 4 sequencing in genie error path
schickling-assistant Jul 29, 2026
f2898b6
fix: feed oxfmt through Effect 4 process stdin
schickling-assistant Jul 29, 2026
1a9f668
fix: run oxfmt through Effect 4 process spawner
schickling-assistant Jul 29, 2026
9434e61
fix: use Effect 4 duration input decoder
schickling-assistant Jul 29, 2026
4140e48
fix: use Effect 4 semaphore constructor
schickling-assistant Jul 29, 2026
866a4ca
fix: use Effect 4 stream acquire loop
schickling-assistant Jul 29, 2026
c26760c
fix: use Effect 4 stream recovery
schickling-assistant Jul 29, 2026
96d8ba3
fix: use Effect 4 defect APIs in genie path
schickling-assistant Jul 29, 2026
5e88461
fix: recognize Effect 4 platform not found errors
schickling-assistant Jul 29, 2026
8e0fedf
fix: use Effect 4 schema codecs in file locks
schickling-assistant Jul 29, 2026
4e5e3ee
fix: refresh generated pnpm manifests
schickling-assistant Jul 29, 2026
02ca266
fix: refresh pnpm lock and oxlint FOD
schickling-assistant Jul 29, 2026
51fa7a9
fix: sweep Effect 4 schema constructor shapes
schickling-assistant Jul 29, 2026
f98f9fa
fix: complete Effect 4 genie run
schickling-assistant Jul 29, 2026
c60ad66
fix(effect4): preserve http header span allowlist
schickling-assistant Jul 28, 2026
6a7fea2
fix: refresh B3 patched dependency outputs
schickling-assistant Jul 29, 2026
6730804
merge: bring main recipes into Effect 4 flip branch
schickling-assistant Jul 29, 2026
16f2239
docs(effect-4): close compiler-derived recipe gaps (#1042)
schickling-assistant Jul 29, 2026
dc75ee2
fix(nix): refresh Effect 4 package FOD hashes
schickling-assistant Jul 29, 2026
38bff83
docs(effect-4): complete shared recipe corpus
schickling-assistant Jul 29, 2026
74154c2
docs(effect-4): record patch FOD authority boundary
schickling-assistant Jul 29, 2026
0404f5f
docs(effect-4): route restate migration recipes
schickling-assistant Jul 29, 2026
222161f
feat(effect-path): port to Effect 4
schickling-assistant Jul 29, 2026
e247bb4
Port tui-react to Effect 4 beta.102
schickling-assistant Jul 29, 2026
2011ba9
Update Effect 4 schema error baseline
schickling-assistant Jul 29, 2026
4fde589
fix(content-address): port Effect 4 result and schema contracts
schickling-assistant Jul 29, 2026
a8b5f30
fix(kdl): preserve numeric location schema under Effect 4
schickling-assistant Jul 29, 2026
5c57382
fix(kdl-effect): port schema transformations to Effect 4
schickling-assistant Jul 29, 2026
fbcb9af
fix(effect-distributed-lock): port semaphore filtering to Effect 4
schickling-assistant Jul 29, 2026
407cc2c
Port otel contract core to Effect 4
schickling-assistant Jul 29, 2026
4425cd6
Preserve UUID attribute acceptance on Effect 4
schickling-assistant Jul 29, 2026
8d21130
Align otel contract assertions with Effect 4
schickling-assistant Jul 29, 2026
dd3924a
Document UUID to GUID schema migration
schickling-assistant Jul 29, 2026
da4015f
docs(changelog): record Effect 4 package ports
schickling-assistant Jul 29, 2026
7ef722c
merge: bring main (#1038) into the Effect 4 flip branch
schickling-assistant Jul 30, 2026
83f5ed0
docs(effect-4): add the D1 CI expected-failure manifest
schickling-assistant Jul 30, 2026
9394b3f
docs(effect-4): correct the weaver attribution in the D1 manifest
schickling-assistant Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ All notable changes to this project will be documented in this file.
provider layer construction/provision, scope teardown, retry behavior, and
external-store subscriber observations.

### Changed

- Port **@overeng/effect-path**, **@overeng/tui-react**,
**@overeng/content-address**, **@overeng/kdl**, **@overeng/kdl-effect**,
**@overeng/effect-distributed-lock**, and **@overeng/otel-contract** to the
Effect 4 beta.102 APIs while preserving their established runtime contracts.

### Fixed

- **@overeng/genie/package-json**: allow consumers to materialize selected
Expand Down
17 changes: 17 additions & 0 deletions context/effect-4/alignment-register.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,23 @@
- **Status:** allowlisted as a negative-control diff in
`patterns/fork-defaults/scenario.json`.

## effect-path-public-result-shape

- **Difference:** Public in-memory `Sandbox.validate`, `Sandbox.resolve`, and `validatePath` results
follow Effect's major-version data type: v3 `Either` used `_tag: "Right" | "Left"` with
`right` / `left` fields, while v4 `Result` uses `_tag: "Success" | "Failure"` with
`success` / `failure` fields.
- **Proposed decision:** Accept the v4 `Result` shape. These APIs deliberately expose an
Effect-owned type, and preserving the removed v3 `Either` shape would require a local
compatibility dialect.
- **Blast radius:** `@overeng/effect-path` sandbox validation APIs and consumers that inspect the
returned in-memory Effect data type.
- **Audit evidence:** Repository-wide consumer searches found no downstream call site for these
three APIs and no serialization, snapshot-write, cache, or persistence boundary for their result
objects. Package baselines normalize the branch to its success or failure value.
- **Status:** ACCEPTED for in-memory public API only. Any future serialization site requires a
separate wire-format decision.

## equality-structural-default

- **Difference:** v4 structurally compares plain objects, arrays, maps, sets,
Expand Down
8 changes: 8 additions & 0 deletions context/effect-4/baseline-operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ A dev-dependency addition invalidates every lockfile-derived fixed-output deriva
them (`flake.nix:199-214`). `evergreen fod chase-fod-closure` cannot help: it requires
`passthru.evergreen.fodGraph.v1` metadata the consumers lack. Refresh per attr instead.

Patched dependencies are part of the same authority boundary. Treat `.patch` files as opaque generated
artifacts: put live-migration markers on the wiring that references a patch, never inside its
whitespace-sensitive body. Adding, removing, or regenerating a patch changes the bytes included by the
`oxc-config-plugin` FOD and changes the patch hashes throughout `pnpm-lock.yaml`. Regenerate the
lockfile first, prove every patch still applies, and then refresh all eight attrs. A branch merge that
combines patch changes needs another refresh against the merged graph; updating only the first hash
reported by Nix leaves the remaining boundaries stale.

Package attrs:

```sh
Expand Down
146 changes: 146 additions & 0 deletions context/effect-4/ci-expected-failures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# CI expected-failure manifest — Effect 4 integration branch

Decision **D1** for #925. This file is the gate for the intentionally-red integration branch.

## The gate

**CI must run on every push, and the failure set must equal this manifest.**

Two conditions break it, and the second is the one this file exists for:

1. a failure appears that is not listed here, or a listed failure starts passing without this file
being updated;
2. **CI does not run at all.**

Condition 2 is not hypothetical. This branch went **16 commits with no CI whatsoever** because it had
become unmergeable against `main`, which silently suppresses `pull_request` workflows. Nobody noticed,
because the branch was documented as "intentionally red" and slice PRs as "not judgeable by repo CI" —
so *"CI is not running"* and *"CI is red, as expected"* were indistinguishable. A gate that cannot
distinguish **"did not run"** from **"ran and failed"** protects nothing.

## Baseline

| | |
|---|---|
| run | **`30548813585`** |
| commit | **`7ef722c3b`** |
| result | 15 failure · 8 success · 2 skipped |
| comparison base | **`main` @ `5f3c78a9f`, run `30461270774` — 25/25 SUCCESS** |

**`main` is fully green, so there is no base noise to subtract and every failure below is
flip-attributable.** This matters for interpreting the manifest later: "expected" here means
*"caused by the migration and understood"*, never *"we gave up on it"*.

Do not repeat the epic body's phrase *"the base fails independently on `test-live-deploy-ci-tools`,
`pnpm-builder-contract`, `test-integration-restate` and the FOD checks"* without qualification. It
refers to the **flip base**, not `main`; all four are green on `main`. Read as "main is red", it would
license discarding genuine failures as pre-existing.

## Green — and two of these are load-bearing

```
bootstrap-cold-proof bundle-smoke cargo default-ref-policy
deploy-storybooks source-shape
nix-fod-check (namespace-profile-linux-x86-64)
nix-fod-check (namespace-profile-macos-arm64)
```

- **`nix-fod-check` green on both legs** is real evidence the eight-attr FOD refresh on this branch is
correct. That was the expensive open risk.
- **`bootstrap-cold-proof` green** means the genie bootstrap closure still realizes — the deadlock that
bridge B8 exists to hold open has not regressed.

## Expected failures — attributed

Cause is the flip: ~52% of the Effect surface is still unported, so anything compiling, linting,
building or testing the workspace fails.

| job | attribution |
|---|---|
| `typecheck` | unported packages; the measured scope was 6,345 de-duplicated TS errors |
| `lint` | unported source |
| `test (namespace-profile-linux-x86-64)` | unported packages fail to compile/collect |
| `test (namespace-profile-macos-arm64)` | as above |
| `nix-check (namespace-profile-linux-x86-64)` | builds packages from source that does not compile |
| `nix-check (namespace-profile-macos-arm64)` | as above |
| `pnpm-builder-contract` | workspace build of unported packages |
| `pnpm-regression` | as above |
| `test-live-deploy-ci-tools` | `ci-tools` unported; this is the package bridge **B8** pins pre-flip |
| `test-megarepo-cold-gc` | `megarepo` unported (largest slice, 1,388 → 550 errors on its branch) |
| `test-integration-notion` | Notion chain unported (228 Effect files) — confirmed green on `main`, so flip-induced |
| `test-integration-restate` | **designed to fail at the flip.** Baseline #985 pins the v3 parser text inside Restate's HTTP 400 body; v4's `SchemaError(...)` changes it. Mitigation tracked in #978. Do **not** rebaseline. |
| `weaver` | runs an `otel-contract` e2e that imports `@overeng/utils-dev/otelite`; **`utils-dev` is unported** and `src/otelite/Otelite.ts:127` calls `Schema.decodeUnknown`, removed in v4. Clears when `utils-dev` (#1039) lands. See below. |

### `weaver` — traced, and it needs NO new scope item

An earlier version of this file called `weaver` an *unscoped port gap* requiring a new Phase 4 entry.
**That was wrong.** Traced properly:

- `weaver:live-check` (`nix/devenv-modules/tasks/shared/weaver-live-check.nix:29,31`) runs a vitest e2e
in **`packages/@overeng/otel-contract`** — `src/registry-live-check.integration.test.ts`
- that test imports **`@overeng/utils-dev/otelite`**
- **`utils-dev` is unported** on this tip, and `src/otelite/Otelite.ts:127` calls `Schema.decodeUnknown`
- hence `TypeError: Schema.decodeUnknown is not a function`

So it is an ordinary flip-attributable failure caused by an **unported dependency**, not by `weaver`
and not by `otel-contract`. `Otelite.ts:127` is a blocker #925 already names. It clears when `utils-dev`
(#1039) lands. **Do not add a weaver item to Phase 4.**

**Checked while here — the enumeration is not structurally incomplete.** Outside
`packages/@overeng/`, the only Effect-importing files on this tip are under `context/` (recipes, docs,
examples); no build lane is missed by the wave lists.

**Why this correction is itself the useful part:** `otel-contract` is one of the seven packages that
landed by cherry-pick with no CI, and this red says **nothing** about whether `otel-contract` is sound
— it fails on a dependency before reaching it. That is a live instance of the caveat in the last
section, and the reason per-package runs with a non-zero collected count are still owed.

## UNEXAMINED — must be triaged before they count as expected

These failed and have **not** been root-caused. They are recorded so they are not silently absorbed.

- `nix-closure-sizes`
- `devenv-perf`

**An unexamined entry is indistinguishable from a known one once it is written down.** Both are
plausibly flip-attributable (closure sizes shift when a whole dependency cohort moves; perf lanes
build the workspace), but *plausible* is not *attributed*. Neither may be treated as expected until
triaged, and either could be masking a real regression.

- [ ] triage `nix-closure-sizes`
- [ ] triage `devenv-perf`

## Skipped, not failures

`ci/measurements-report`, `notify-alignment` — conditional jobs.

## What this manifest does NOT establish

**It does not verify the seven already-landed packages** (`effect-path`, `tui-react`,
`content-address`, `kdl`, `kdl-effect`, `effect-distributed-lock`, `otel-contract`). Those landed by
cherry-pick with no CI at all, and this run does not close that:

- `test:<pkg>` pulls a dependency batch that can fail **before reaching** the package under test —
the normal case while half the graph is unported;
- a red aggregate check says nothing about whether a given package's tests actually **ran**.

Closing it requires per-package runs:

```sh
CI=1 devenv tasks run test:<pkg> --mode single --show-output --no-tui
jq -r '.adapter.records[] | select(._tag=="Metric") | "\(.name)=\(.value)"' <summary.json>
```

Read counts from `adapter.records` — there is **no** top-level `vitest.tests`, and a top-level lookup
returns `null`, which reads as "no data" and is one step from being reported as zero. Also read
`child.exit_code` (distinct from the task runner's) and `degraded`.

**A suite collecting zero tests exits 0 and replays GREEN.** Per #1023 that does not weaken a gate, it
removes it — indistinguishable from passing. Any per-package verification must assert a **non-zero
collected count**.

## Maintenance

Update this file in the **same commit** as any change to the failure set, with the reason. As slices
land, entries move from failing to green and are deleted here — **the manifest shrinking is the
progress metric.** When it is empty, the branch is green and D1 is satisfied.
36 changes: 36 additions & 0 deletions context/effect-4/recipes/config-error-collapse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Pattern: config-error-collapse

**Area:** Config errors **Kind:** conditional

## Shape change

Effect 3 exposed `ConfigError` as a union of variants such as `MissingData`, `InvalidData`, `And`,
and `Or`. Effect 4 beta.102 exposes one `Config.ConfigError` class with `_tag: "ConfigError"`.

## Mapping

For sites that only carry or catch the error opaquely, replace the v3 module/type with
`Config.ConfigError`:

```ts
import { Config } from "effect"

declare const layer: Layer.Layer<Service, Config.ConfigError>
```

## Mandatory stop condition

Before applying the import/type mapping, inspect every affected site. If a site discriminates a v3
variant, reads variant-specific fields, or selectively recovers one variant, **stop**. The variants
have collapsed and there is no faithful mechanical mapping.

## Verification

Checked against `effect@4.0.0-beta.102` `Config.ts`. In `@overeng/restate-effect`, all seven affected
sites were type-only or opaque and none discriminated a v3 variant, so the mapping was safe there.

## Gotchas

- A green typecheck after broadening handling to the single v4 tag can conceal changed recovery
behavior. The site audit is required.
- Do not invent replacement tags for the removed variants.
39 changes: 39 additions & 0 deletions context/effect-4/recipes/datetime-values.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Pattern: datetime-values

**Area:** DateTime values **Kind:** renames **Our usage:** construction and telemetry timestamp
access in `agent-session-ingest`, `otel-contract`, and `utils`.

## Migration table

| v3 | v4 |
| ------------------------ | ---------------------------- |
| `DateTime.unsafeMake(x)` | `DateTime.makeUnsafe(x)` |
| `dateTime.epochMillis` | `dateTime.epochMilliseconds` |

These are value-level `DateTime` APIs. Schema wire codecs are covered by the separate
`schema-date` recipe; do not confuse a value constructor rename with the surviving-name schema
hazard.

## Equivalence

Both replacements are **VERIFIED** against the real `effect@3.21.4` and
`effect@4.0.0-beta.102` tarballs. A cross-major probe constructed values from epoch zero, an ISO
timestamp, and a native `Date`. Epoch milliseconds and formatted ISO output matched exactly for
all inputs.

## Intended differences

None.

## Gotchas

- The `unsafe` word moved; it was not removed. `makeUnsafe` can still throw for invalid input.
- Do not replace `unsafeMake` with safe `make` without handling its result type.
- `Schema.DateTimeUtc` is a different migration: v3's string codec becomes
`Schema.DateTimeUtcFromString`, because bare v4 `Schema.DateTimeUtc` validates an existing
`DateTime.Utc` value.

## Codemod rule

Both value-level renames are mechanical. Review every `Schema.DateTime*` occurrence using the
wire-vs-value pattern in `schema-date`.
41 changes: 41 additions & 0 deletions context/effect-4/recipes/duration-input.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Pattern: duration-input

**Area:** Duration **Kind:** renames **Our usage:** 10 workspace compiler diagnostics for
`Duration.DurationInput` on the current flip snapshot.

## Migration table

| v3 | v4 |
| ------------------------ | --------------------------------- |
| `Duration.DurationInput` | `Duration.Input` |
| `Duration.decode(input)` | `Duration.fromInputUnsafe(input)` |

V4's `fromInputUnsafe` is the replacement for the total, typed-input v3 `decode`. Keep
`Duration.fromInput` for unknown input that must report invalid data rather than throw.

## Equivalence

The aliases, signatures, and replacement are **VERIFIED** against the real `effect@3.21.4` and
`effect@4.0.0-beta.102` tarballs. A cross-major probe compared milliseconds for the repository's
shared input forms: number, bigint nanoseconds, seconds/nanoseconds tuple, duration string, and
infinity. Every result matched.

V4 `Input` additionally accepts duration objects and explicit infinity strings. That widening does
not change existing v3-valid callers.

## Intended differences

None for existing inputs.

## Gotchas

- `fromInputUnsafe` is appropriate because the static `Input` type guarantees the accepted shape.
Do not use it at an `unknown` boundary.
- A bigint duration input is nanoseconds in both versions, not milliseconds.
- Do not replace a `Duration.decodeUnknown` boundary mechanically; its validation/result shape
needs separate handling.

## Codemod rule

The type and typed constructor renames are mechanical. Unknown-input parsers require per-site
review.
Loading
Loading