diff --git a/.changeset/sleep-then-to-delay.md b/.changeset/sleep-then-to-delay.md
new file mode 100644
index 00000000..fba8ee82
--- /dev/null
+++ b/.changeset/sleep-then-to-delay.md
@@ -0,0 +1,5 @@
+---
+"@effect/tsgo": patch
+---
+
+Add `sleepThenEffectToDelay` diagnostic (`TS377133`) to suggest using `Effect.delay` instead of sequencing `Effect.sleep` before an effect.
diff --git a/README.md b/README.md
index b3778c5c..72e61399 100644
--- a/README.md
+++ b/README.md
@@ -163,6 +163,7 @@ Some diagnostics are off by default or have a default severity of suggestion, bu
schemaStructWithTag | Suggests using Schema.TaggedStruct instead of Schema.Struct with _tag field |
schemaUnionOfLiterals | Suggests combining multiple Schema.Literal calls in Schema.Union into a single Schema.Literal |
serviceNotAsClass | Warns when Context.Service is used as a variable instead of a class declaration |
+ sleepThenEffectToDelay | Sequencing Effect.sleep before an effect re-implements Effect.delay |
strictBooleanExpressions | Enforces boolean types in conditional expressions for type safety |
syncToSucceed | Suggests using Effect.succeed instead of Effect.sync when the thunk returns a constant value |
timeoutCatchTagToTimeoutOrElse | Suggests dedicated timeout combinators instead of catching TimeoutError immediately after Effect.timeout |
diff --git a/_packages/tsgo/src/metadata.json b/_packages/tsgo/src/metadata.json
index b4483bac..07ec4279 100644
--- a/_packages/tsgo/src/metadata.json
+++ b/_packages/tsgo/src/metadata.json
@@ -2526,6 +2526,30 @@
]
}
},
+ {
+ "name": "sleepThenEffectToDelay",
+ "group": "style",
+ "description": "Sequencing Effect.sleep before an effect re-implements Effect.delay",
+ "defaultSeverity": "suggestion",
+ "fixable": false,
+ "supportedEffect": [
+ "v3",
+ "v4"
+ ],
+ "codes": [
+ 377133
+ ],
+ "preview": {
+ "sourceText": "import { Effect } from \"effect\"\n\ndeclare const sendRequest: Effect.Effect\u003cvoid\u003e\n\nexport const throttled = Effect.sleep(\"1 second\").pipe(\n Effect.andThen(sendRequest)\n)\n",
+ "diagnostics": [
+ {
+ "start": 139,
+ "end": 153,
+ "text": "Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)"
+ }
+ ]
+ }
+ },
{
"name": "strictBooleanExpressions",
"group": "style",
diff --git a/docs/rules/sleep-then-effect-to-delay.md b/docs/rules/sleep-then-effect-to-delay.md
new file mode 100644
index 00000000..47074153
--- /dev/null
+++ b/docs/rules/sleep-then-effect-to-delay.md
@@ -0,0 +1,67 @@
+
+
+# `sleepThenEffectToDelay`
+
+Sequencing Effect.sleep before an effect re-implements Effect.delay
+
+| Property | Value |
+| --- | --- |
+| Category | Style |
+| Default severity | `suggestion` |
+| Fixable | No |
+| Effect versions | v3, v4 |
+| Diagnostic codes | `TS377133` |
+| Language Service name | `sleepThenEffectToDelay` |
+| Oxlint name | `effecttsgo/sleep-then-effect-to-delay` |
+
+## Preview
+
+```ts
+import { Effect } from "effect"
+
+declare const sendRequest: Effect.Effect
+
+export const throttled = Effect.sleep("1 second").pipe(
+ Effect.andThen(sendRequest)
+/**
+ ^^^^^^^^^^^^^^ effecttsgo(sleep-then-effect-to-delay): Sequencing Effect.sleep before an effect re-implements Effect.delay.
+*/
+)
+```
+
+## Language Service Configuration
+
+See the [Language Service setup guide](../../README.md#installation) for installation instructions.
+
+```jsonc
+{
+ "$schema": "./node_modules/@effect/tsgo/schema.json",
+ "compilerOptions": {
+ "plugins": [
+ {
+ "name": "@effect/language-service",
+ "diagnosticSeverity": {
+ "sleepThenEffectToDelay": "warning"
+ }
+ }
+ ]
+ }
+}
+```
+
+## Oxlint Configuration
+
+See the [Oxlint setup guide](../README.md#oxlint-setup) for installation and patching instructions.
+
+```json
+{
+ "$schema": "./node_modules/@effect/tsgo/oxlint-schema.json",
+ "options": {
+ "typeAware": true
+ },
+ "plugins": ["effecttsgo"],
+ "rules": {
+ "effecttsgo/sleep-then-effect-to-delay": "warn"
+ }
+}
+```
diff --git a/internal/diagnostics/effectDiagnosticMessages.json b/internal/diagnostics/effectDiagnosticMessages.json
index 73c2c660..925f89d2 100644
--- a/internal/diagnostics/effectDiagnosticMessages.json
+++ b/internal/diagnostics/effectDiagnosticMessages.json
@@ -506,5 +506,9 @@
"This module reference imports `{0}`, which is obsolete in Effect v4. In Effect v4, Schema is provided directly by `Schema` from `effect` (or `effect/Schema`). effect(obsoleteSchemaImport)": {
"category": "Warning",
"code": 377128
+ },
+ "Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)": {
+ "category": "Suggestion",
+ "code": 377133
}
}
diff --git a/internal/rules/rules.go b/internal/rules/rules.go
index ccaa3bee..ed0e6303 100644
--- a/internal/rules/rules.go
+++ b/internal/rules/rules.go
@@ -119,4 +119,5 @@ var All = []rule.Rule{
AcquireReleaseDisposable,
RaceFirstWithSleepToTimeout,
RunOfExitToRunExit,
+ SleepThenEffectToDelay,
}
diff --git a/internal/rules/sleep_then_effect_to_delay.go b/internal/rules/sleep_then_effect_to_delay.go
new file mode 100644
index 00000000..20240f26
--- /dev/null
+++ b/internal/rules/sleep_then_effect_to_delay.go
@@ -0,0 +1,234 @@
+package rules
+
+import (
+ "github.com/effect-ts/tsgo/etscore"
+ "github.com/effect-ts/tsgo/internal/rule"
+ "github.com/effect-ts/tsgo/internal/typeparser"
+ "github.com/microsoft/TypeScript/tsc/shim/ast"
+ "github.com/microsoft/TypeScript/tsc/shim/checker"
+ "github.com/microsoft/TypeScript/tsc/shim/core"
+ tsdiag "github.com/microsoft/TypeScript/tsc/shim/diagnostics"
+ "github.com/microsoft/TypeScript/tsc/shim/scanner"
+)
+
+// SleepThenEffectToDelay suggests using Effect.delay instead of sequencing Effect.sleep before an effect.
+var SleepThenEffectToDelay = rule.Rule{
+ Name: "sleepThenEffectToDelay",
+ Group: "style",
+ Description: "Sequencing Effect.sleep before an effect re-implements Effect.delay",
+ DefaultSeverity: etscore.SeveritySuggestion,
+ SupportedEffect: []string{"v3", "v4"},
+ Codes: []int32{
+ tsdiag.Sequencing_Effect_sleep_before_an_effect_re_implements_Effect_delay_effect_sleepThenEffectToDelay.Code(),
+ },
+ Run: func(ctx *rule.Context) []*ast.Diagnostic {
+ matches := AnalyzeSleepThenEffectToDelay(ctx.TypeParser, ctx.Checker, ctx.SourceFile)
+ diags := make([]*ast.Diagnostic, len(matches))
+ for i, match := range matches {
+ diags[i] = ctx.NewDiagnostic(
+ match.SourceFile,
+ match.Location,
+ tsdiag.Sequencing_Effect_sleep_before_an_effect_re_implements_Effect_delay_effect_sleepThenEffectToDelay,
+ nil,
+ )
+ }
+ return diags
+ },
+}
+
+// SleepThenEffectToDelayMatch holds match details for diagnostic emission.
+type SleepThenEffectToDelayMatch struct {
+ SourceFile *ast.SourceFile
+ Location core.TextRange
+ Callee *ast.Node
+}
+
+// AnalyzeSleepThenEffectToDelay finds sequencing calls where Effect.sleep is the first operand.
+func AnalyzeSleepThenEffectToDelay(tp *typeparser.TypeParser, c *checker.Checker, sf *ast.SourceFile) []SleepThenEffectToDelayMatch {
+ if tp == nil || c == nil || sf == nil {
+ return nil
+ }
+
+ var matches []SleepThenEffectToDelayMatch
+ visitedCallees := make(map[core.TextRange]bool)
+
+ flows := tp.PipingFlows(sf, true)
+ for _, flow := range flows {
+ if flow == nil {
+ continue
+ }
+
+ var sequencer *typeparser.PipingFlowTransformation
+
+ if isDirectEffectSleepCall(tp, flow.Subject.Node) {
+ if len(flow.Transformations) > 0 {
+ sequencer = &flow.Transformations[0]
+ }
+ } else if len(flow.Transformations) > 1 && isEffectSleepTransformation(tp, &flow.Transformations[0]) {
+ sequencer = &flow.Transformations[1]
+ }
+
+ if sequencer == nil || sequencer.Callee == nil {
+ continue
+ }
+
+ if !isSequencerTransformation(tp, c, sequencer) {
+ continue
+ }
+
+ loc := scanner.GetErrorRangeForNode(sf, sequencer.Callee)
+ if visitedCallees[loc] {
+ continue
+ }
+ visitedCallees[loc] = true
+
+ matches = append(matches, SleepThenEffectToDelayMatch{
+ SourceFile: sf,
+ Location: loc,
+ Callee: sequencer.Callee,
+ })
+ }
+
+ return matches
+}
+
+func isDirectEffectSleepCall(tp *typeparser.TypeParser, node *ast.Node) bool {
+ if node == nil {
+ return false
+ }
+ node = ast.SkipParentheses(node)
+ if node.Kind != ast.KindCallExpression {
+ return false
+ }
+ call := node.AsCallExpression()
+ if call == nil || call.Expression == nil {
+ return false
+ }
+ return tp.IsNodeReferenceToEffectModuleApi(call.Expression, "sleep")
+}
+
+func isEffectSleepTransformation(tp *typeparser.TypeParser, transformation *typeparser.PipingFlowTransformation) bool {
+ if transformation == nil || transformation.Callee == nil {
+ return false
+ }
+ return tp.IsNodeReferenceToEffectModuleApi(transformation.Callee, "sleep")
+}
+
+func isSequencerTransformation(tp *typeparser.TypeParser, c *checker.Checker, transformation *typeparser.PipingFlowTransformation) bool {
+ if tp == nil || c == nil || transformation == nil || transformation.Callee == nil || len(transformation.Args) == 0 {
+ return false
+ }
+ callee := transformation.Callee
+ arg := transformation.Args[0]
+ if arg == nil {
+ return false
+ }
+
+ switch {
+ case tp.IsNodeReferenceToEffectModuleApi(callee, "andThen"):
+ t := tp.GetTypeAtLocation(arg)
+ return sleepContinuationIsEffect(tp, t)
+
+ case tp.IsNodeReferenceToEffectModuleApi(callee, "zipRight"):
+ t := tp.GetTypeAtLocation(arg)
+ return sleepContinuationIsEffect(tp, t)
+
+ case tp.IsNodeReferenceToEffectModuleApi(callee, "flatMap"):
+ return isCallbackIgnoringParam(tp, c, arg)
+
+ default:
+ return false
+ }
+}
+
+func isCallbackIgnoringParam(tp *typeparser.TypeParser, c *checker.Checker, callbackNode *ast.Node) bool {
+ if callbackNode == nil {
+ return false
+ }
+ callbackNode = ast.SkipParentheses(callbackNode)
+ var params *ast.NodeList
+ var body *ast.Node
+
+ switch callbackNode.Kind {
+ case ast.KindArrowFunction:
+ fn := callbackNode.AsArrowFunction()
+ params = fn.Parameters
+ body = fn.Body
+ case ast.KindFunctionExpression:
+ fn := callbackNode.AsFunctionExpression()
+ params = fn.Parameters
+ body = fn.Body
+ default:
+ return false
+ }
+
+ if body == nil {
+ return false
+ }
+
+ paramCount := 0
+ if params != nil {
+ paramCount = len(params.Nodes)
+ }
+
+ if paramCount == 0 {
+ return true
+ }
+
+ if paramCount == 1 {
+ paramDecl := params.Nodes[0].AsParameterDeclaration()
+ if paramDecl == nil || paramDecl.Name() == nil || paramDecl.DotDotDotToken != nil {
+ return false
+ }
+ nameNode := paramDecl.Name()
+ if nameNode.Kind != ast.KindIdentifier {
+ return false
+ }
+ paramSymbol := tp.GetSymbolAtLocation(nameNode)
+ if paramSymbol == nil {
+ return true
+ }
+ return !sleepParamIsReferenced(tp, c, paramSymbol, body)
+ }
+
+ return false
+}
+
+// sleepParamIsReferenced checks if the given parameter symbol is referenced in the node tree.
+func sleepParamIsReferenced(tp *typeparser.TypeParser, c *checker.Checker, paramSymbol *ast.Symbol, body *ast.Node) bool {
+ var usesParameter func(node *ast.Node) bool
+ usesParameter = func(node *ast.Node) bool {
+ if node == nil {
+ return false
+ }
+ if node.Kind == ast.KindShorthandPropertyAssignment && c.GetShorthandAssignmentValueSymbol(node) == paramSymbol {
+ return true
+ }
+ if node.Kind == ast.KindIdentifier {
+ sym := tp.GetSymbolAtLocation(node)
+ if sym != nil && (sym == paramSymbol || checker.Checker_getSymbolIfSameReference(c, sym, paramSymbol) != nil) {
+ return true
+ }
+ }
+ return node.ForEachChild(usesParameter)
+ }
+ return usesParameter(body)
+}
+
+// sleepContinuationIsEffect checks whether all constituents of the type (unrolling unions)
+// satisfy the Effect variance interface.
+func sleepContinuationIsEffect(tp *typeparser.TypeParser, t *checker.Type) bool {
+ if tp == nil || t == nil {
+ return false
+ }
+ members := tp.UnrollUnionMembers(t)
+ if len(members) == 0 {
+ return false
+ }
+ for _, m := range members {
+ if !tp.IsEffectType(m) {
+ return false
+ }
+ }
+ return true
+}
diff --git a/shim/diagnostics/shim.go b/shim/diagnostics/shim.go
index f93ae1fd..e9b4e666 100644
--- a/shim/diagnostics/shim.go
+++ b/shim/diagnostics/shim.go
@@ -1544,6 +1544,7 @@ var Searching_all_ancestor_node_modules_directories_for_preferred_extensions_Col
var See_also_https_Colon_Slash_Slashaka_ms_Slashtsconfig_Slashmodule = diagnostics.See_also_https_Colon_Slash_Slashaka_ms_Slashtsconfig_Slashmodule
var Selection_is_not_a_valid_statement_or_statements = diagnostics.Selection_is_not_a_valid_statement_or_statements
var Selection_is_not_a_valid_type_node = diagnostics.Selection_is_not_a_valid_type_node
+var Sequencing_Effect_sleep_before_an_effect_re_implements_Effect_delay_effect_sleepThenEffectToDelay = diagnostics.Sequencing_Effect_sleep_before_an_effect_re_implements_Effect_delay_effect_sleepThenEffectToDelay
var Service_0_is_required_but_not_provided_by_dependencies_effect_missingEffectServiceDependency = diagnostics.Service_0_is_required_but_not_provided_by_dependencies_effect_missingEffectServiceDependency
var Services_0_are_required_but_not_provided_by_dependencies_effect_missingEffectServiceDependency = diagnostics.Services_0_are_required_but_not_provided_by_dependencies_effect_missingEffectServiceDependency
var Set_the_JavaScript_language_version_for_emitted_JavaScript_and_include_compatible_library_declarations = diagnostics.Set_the_JavaScript_language_version_for_emitted_JavaScript_and_include_compatible_library_declarations
diff --git a/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.errors.txt b/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.errors.txt
new file mode 100644
index 00000000..995e8041
--- /dev/null
+++ b/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.errors.txt
@@ -0,0 +1,26 @@
+=== Metadata ===
+Effect version: 3.19.19
+
+
+
+==== /.src/sleepThenEffectToDelay.ts (0 errors) ====
+ // @effect-v3
+ // @effect-diagnostics *:off
+ // @effect-diagnostics sleepThenEffectToDelay:suggestion
+ import { Effect } from "effect"
+
+ declare const sendRequest: Effect.Effect
+
+ // Negative control: GOOD forms only
+ export const goodDelayPipe = sendRequest.pipe(Effect.delay("1 second"))
+ export const goodDelayDataFirst = Effect.delay(sendRequest, "100 millis")
+
+ export const goodGenStandalone = Effect.gen(function* () {
+ yield* Effect.sleep("1 second")
+ return yield* sendRequest
+ })
+
+ export const goodParamUsingCallback = Effect.sleep("1 second").pipe(
+ Effect.flatMap((val) => Effect.succeed(val))
+ )
+
diff --git a/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.flows.sleepThenEffectToDelay.mermaid b/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.flows.sleepThenEffectToDelay.mermaid
new file mode 100644
index 00000000..3da70f8b
--- /dev/null
+++ b/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.flows.sleepThenEffectToDelay.mermaid
@@ -0,0 +1,41 @@
+flowchart TB
+ 0[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 1["type: Effect#lt;void, never, never#gt;
callee: Effect.delay
args: #91;#quot;1 second#quot;#93;"]
+ 2[/"type: #123; #40;duration: DurationInput#41;: #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;A, E, R#gt;; #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;, duration: DurationInput#41;: Effect#lt;A, E, R#gt;; #125;
node: Effect.delay"/]
+ 3[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 4[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 5["type: Effect#lt;void, never, never#gt;
callee:
args: #91;#93;"]
+ 6[/"type: #123; #40;duration: DurationInput#41;: #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;A, E, R#gt;; #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;, duration: DurationInput#41;: Effect#lt;A, E, R#gt;; #125;
node: Effect.delay"/]
+ 7[/"type: #quot;100 millis#quot;
node: #quot;100 millis#quot;"/]
+ 8((("type: Effect#lt;void, never, never#gt;
node: Effect.gen#40;function* #40;#41; #123;#92;n yield* Effect.sleep#40;#quot;1 second#quot;#41;#92;n return yield* sendRequest#92;n#125;#41;")))
+ 9[/"type: void
node: yield* sendRequest"/]
+ 10[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 11["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 12[/"type: #40;duration: DurationInput#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 13[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 14["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 15[/"type: #40;duration: DurationInput#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 16["type: Effect#lt;void, never, never#gt;
callee: Effect.flatMap
args: #91;#40;val#41; =#gt; Effect.succeed#40;val#41;#93;"]
+ 17[/"type: #123; #lt;A, B, E1, R1#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E1, R #124; R1#gt;; #lt;A, E, R, B, E1, R1#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: Effect#lt;B, E #124; E1, R #124; R1#gt;; #125;
node: Effect.flatMap"/]
+ 18[["type: #40;val: void#41; =#gt; Effect#lt;void, never, never#gt;
node: #40;val#41; =#gt; Effect.succeed#40;val#41;"]]
+ 19[/"type: void
node: val"/]
+ 20["type: Effect#lt;void, never, never#gt;
callee: Effect.succeed
args: #91;#93;"]
+ 21[/"type: #lt;A#gt;#40;value: A#41; =#gt; Effect#lt;A, never, never#gt;
node: Effect.succeed"/]
+ 2 -->|"kind: transformCallee"| 1
+ 3 -->|"kind: transformArg"| 1
+ 0 -->|"kind: pipe"| 1
+ 4 -->|"kind: pipe"| 5
+ 6 -->|"kind: transformCallee"| 4
+ 7 -->|"kind: transformArg"| 4
+ 9 -->|"kind: potentialReturn"| 8
+ 10 -->|"kind: pipe"| 11
+ 12 -->|"kind: transformCallee"| 11
+ 11 -->|"kind: yieldable"| 8
+ 13 -->|"kind: pipe"| 14
+ 15 -->|"kind: transformCallee"| 14
+ 17 -->|"kind: transformCallee"| 16
+ 19 -->|"kind: pipe"| 20
+ 21 -->|"kind: transformCallee"| 20
+ 20 -->|"kind: potentialReturn"| 18
+ 18 -->|"kind: transformArg"| 16
+ 14 -->|"kind: pipe"| 16
\ No newline at end of file
diff --git a/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.flows.txt b/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.flows.txt
new file mode 100644
index 00000000..7579d164
--- /dev/null
+++ b/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.flows.txt
@@ -0,0 +1 @@
+/.src/sleepThenEffectToDelay.ts -> sleepThenEffectToDelay.flows.sleepThenEffectToDelay.mermaid
diff --git a/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.layers.txt b/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.layers.txt
new file mode 100644
index 00000000..eb56d913
--- /dev/null
+++ b/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.layers.txt
@@ -0,0 +1 @@
+==== /.src/sleepThenEffectToDelay.ts (0 layer exports) ====
diff --git a/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.pipings.txt b/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.pipings.txt
new file mode 100644
index 00000000..72984f06
--- /dev/null
+++ b/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.pipings.txt
@@ -0,0 +1,89 @@
+==== /.src/sleepThenEffectToDelay.ts (6 flows) ====
+
+=== Piping Flow ===
+Location: 9:29 - 9:72
+Node: sendRequest.pipe(Effect.delay("1 second"))
+Node Kind: KindCallExpression
+
+Subject: sendRequest
+Subject Type: Effect
+
+Transformations (1):
+ [0] kind: pipeable
+ callee: Effect.delay
+ args: ["1 second"]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 10:34 - 10:74
+Node: Effect.delay(sendRequest, "100 millis")
+Node Kind: KindCallExpression
+
+Subject: sendRequest
+Subject Type: Effect
+
+Transformations (1):
+ [0] kind: dataFirst
+ callee: Effect.delay
+ args: ["100 millis"]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 12:33 - 15:3
+Node: Effect.gen(function* () {\n yield* Effect.sleep("1 second")\n return yield* sendRequest\n})
+Node Kind: KindCallExpression
+
+Subject: function* () {\n yield* Effect.sleep("1 second")\n return yield* sendRequest\n}
+Subject Type: () => Generator>, void, any>
+
+Transformations (1):
+ [0] kind: call
+ callee: Effect.gen
+ args: (constant)
+ outType: Effect
+
+=== Piping Flow ===
+Location: 13:9 - 13:34
+Node: Effect.sleep("1 second")
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (1):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+
+=== Piping Flow ===
+Location: 17:38 - 19:2
+Node: Effect.sleep("1 second").pipe(\n Effect.flatMap((val) => Effect.succeed(val))\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (2):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: pipeable
+ callee: Effect.flatMap
+ args: [(val) => Effect.succeed(val)]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 18:26 - 18:46
+Node: Effect.succeed(val)
+Node Kind: KindCallExpression
+
+Subject: val
+Subject Type: void
+
+Transformations (1):
+ [0] kind: call
+ callee: Effect.succeed
+ args: (constant)
+ outType: Effect
diff --git a/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.quickfixes.txt b/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.quickfixes.txt
new file mode 100644
index 00000000..dad05bfc
--- /dev/null
+++ b/testdata/baselines/reference/effect-v3/sleepThenEffectToDelay.quickfixes.txt
@@ -0,0 +1,5 @@
+=== Quick Fix Inventory ===
+(no diagnostics)
+
+=== Quick Fix Application Results ===
+(no quick fixes to apply)
diff --git a/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.errors.txt b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.errors.txt
new file mode 100644
index 00000000..dceaa540
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.errors.txt
@@ -0,0 +1,138 @@
+=== Metadata ===
+Effect version: 4.0.0
+
+/.src/sleepThenEffectToDelay.ts(10,3): suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+/.src/sleepThenEffectToDelay.ts(15,3): suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+/.src/sleepThenEffectToDelay.ts(20,3): suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+/.src/sleepThenEffectToDelay.ts(25,3): suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+/.src/sleepThenEffectToDelay.ts(31,3): suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+/.src/sleepThenEffectToDelay.ts(37,3): suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+/.src/sleepThenEffectToDelay.ts(44,3): suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+/.src/sleepThenEffectToDelay.ts(48,36): suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+/.src/sleepThenEffectToDelay.ts(54,36): suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+
+
+==== /.src/sleepThenEffectToDelay.ts (9 errors) ====
+ // @effect-v4
+ // @effect-diagnostics *:off
+ // @effect-diagnostics sleepThenEffectToDelay:suggestion
+ import { Effect, pipe } from "effect"
+
+ declare const sendRequest: Effect.Effect
+
+ // BAD: pipe form with andThen
+ export const badPipeAndThen = Effect.sleep("1 second").pipe(
+ Effect.andThen(sendRequest)
+ ~~~~~~~~~~~~~~
+!!! suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ )
+
+ // BAD: pipe form with flatMap (0 params)
+ export const badPipeFlatMap = Effect.sleep("1 second").pipe(
+ Effect.flatMap(() => sendRequest)
+ ~~~~~~~~~~~~~~
+!!! suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ )
+
+ // BAD: pipe form with flatMap (unused param _)
+ export const badPipeFlatMapUnderscore = Effect.sleep("1 second").pipe(
+ Effect.flatMap((_) => sendRequest)
+ ~~~~~~~~~~~~~~
+!!! suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ )
+
+ // BAD: pipe form with flatMap (unused named param)
+ export const badPipeFlatMapUnusedNamed = Effect.sleep("1 second").pipe(
+ Effect.flatMap((_unused) => sendRequest)
+ ~~~~~~~~~~~~~~
+!!! suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ )
+
+ // BAD: pipe helper function with andThen
+ export const badPipeHelperAndThen = pipe(
+ Effect.sleep("1 second"),
+ Effect.andThen(sendRequest)
+ ~~~~~~~~~~~~~~
+!!! suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ )
+
+ // BAD: pipe helper function with flatMap
+ export const badPipeHelperFlatMap = pipe(
+ Effect.sleep("1 second"),
+ Effect.flatMap(() => sendRequest)
+ ~~~~~~~~~~~~~~
+!!! suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ )
+
+ // BAD: pipe with sleep transformation step (flow.Transformations[0])
+ export const badPipeSleepStep = pipe(
+ "1 second",
+ Effect.sleep,
+ Effect.andThen(sendRequest)
+ ~~~~~~~~~~~~~~
+!!! suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ )
+
+ // BAD: data-first andThen
+ export const badDataFirstAndThen = Effect.andThen(
+ ~~~~~~~~~~~~~~
+!!! suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ Effect.sleep("1 second"),
+ sendRequest
+ )
+
+ // BAD: data-first flatMap
+ export const badDataFirstFlatMap = Effect.flatMap(
+ ~~~~~~~~~~~~~~
+!!! suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ Effect.sleep("100 millis"),
+ () => sendRequest
+ )
+
+ // GOOD: Effect.delay in pipe form
+ export const goodDelayPipe = sendRequest.pipe(Effect.delay("1 second"))
+
+ // GOOD: Effect.delay data-first form
+ export const goodDelayDataFirst = Effect.delay(sendRequest, "100 millis")
+
+ // GOOD: standalone yield* sleep in Effect.gen
+ export const goodGenStandalone = Effect.gen(function* () {
+ yield* Effect.sleep("1 second")
+ return yield* sendRequest
+ })
+
+ // GOOD: non-sequencer first stage (withLive)
+ declare const withLive: (effect: Effect.Effect) => Effect.Effect
+ export const goodTestClockFirstStage = Effect.sleep("1 second").pipe(
+ withLive,
+ Effect.andThen(sendRequest)
+ )
+
+ // GOOD: non-sequencer first stage (schedule)
+ declare const schedule: (effect: Effect.Effect) => Effect.Effect
+ export const goodScheduleFirstStage = Effect.sleep("1 second").pipe(
+ schedule,
+ Effect.andThen(sendRequest)
+ )
+
+ // GOOD: unrelated sleep function
+ declare const unrelatedSleep: (d: string) => { pipe: (f: any) => any }
+ export const goodUnrelatedSleep = unrelatedSleep("1 second").pipe(
+ Effect.andThen(sendRequest)
+ )
+
+ // GOOD: param-using callback in pipe flatMap
+ export const goodParamUsingCallback = Effect.sleep("1 second").pipe(
+ Effect.flatMap((val) => Effect.succeed(val))
+ )
+
+ // GOOD: param-using callback in data-first flatMap
+ export const goodDataFirstParamUsing = Effect.flatMap(
+ Effect.sleep("1 second"),
+ (val) => Effect.succeed(val)
+ )
+
+
+ // GOOD: standalone sleep without sequencer
+ export const goodStandaloneSleep = Effect.sleep("1 second")
+
diff --git a/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.flows.sleepThenEffectToDelay.mermaid b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.flows.sleepThenEffectToDelay.mermaid
new file mode 100644
index 00000000..14c18593
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.flows.sleepThenEffectToDelay.mermaid
@@ -0,0 +1,207 @@
+flowchart TB
+ 0[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 1["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 2[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 3["type: Effect#lt;void, never, never#gt;
callee: Effect.andThen
args: #91;sendRequest#93;"]
+ 4[/"type: #123; #lt;A, B, E2, R2#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;B, E2, R2#gt;#40;f: Effect#lt;B, E2, R2#gt;#41;: #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #125;
node: Effect.andThen"/]
+ 5[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 6[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 7["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 8[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 9["type: Effect#lt;void, never, never#gt;
callee: Effect.flatMap
args: #91;#40;#41; =#gt; sendRequest#93;"]
+ 10[/"type: #123; #lt;A, B, E1, R1#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E1, R #124; R1#gt;; #lt;A, E, R, B, E1, R1#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: Effect#lt;B, E #124; E1, R #124; R1#gt;; #125;
node: Effect.flatMap"/]
+ 11[["type: #40;#41; =#gt; Effect#lt;void, never, never#gt;
node: #40;#41; =#gt; sendRequest"]]
+ 12[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 13[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 14["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 15[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 16["type: Effect#lt;void, never, never#gt;
callee: Effect.flatMap
args: #91;#40;_#41; =#gt; sendRequest#93;"]
+ 17[/"type: #123; #lt;A, B, E1, R1#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E1, R #124; R1#gt;; #lt;A, E, R, B, E1, R1#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: Effect#lt;B, E #124; E1, R #124; R1#gt;; #125;
node: Effect.flatMap"/]
+ 18[["type: #40;_: void#41; =#gt; Effect#lt;void, never, never#gt;
node: #40;_#41; =#gt; sendRequest"]]
+ 19[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 20[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 21["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 22[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 23["type: Effect#lt;void, never, never#gt;
callee: Effect.flatMap
args: #91;#40;_unused#41; =#gt; sendRequest#93;"]
+ 24[/"type: #123; #lt;A, B, E1, R1#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E1, R #124; R1#gt;; #lt;A, E, R, B, E1, R1#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: Effect#lt;B, E #124; E1, R #124; R1#gt;; #125;
node: Effect.flatMap"/]
+ 25[["type: #40;_unused: void#41; =#gt; Effect#lt;void, never, never#gt;
node: #40;_unused#41; =#gt; sendRequest"]]
+ 26[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 27[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 28["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 29[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 30["type: Effect#lt;void, never, never#gt;
callee: Effect.andThen
args: #91;sendRequest#93;"]
+ 31[/"type: #123; #lt;A, B, E2, R2#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;B, E2, R2#gt;#40;f: Effect#lt;B, E2, R2#gt;#41;: #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #125;
node: Effect.andThen"/]
+ 32[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 33[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 34["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 35[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 36["type: Effect#lt;void, never, never#gt;
callee: Effect.flatMap
args: #91;#40;#41; =#gt; sendRequest#93;"]
+ 37[/"type: #123; #lt;A, B, E1, R1#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E1, R #124; R1#gt;; #lt;A, E, R, B, E1, R1#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: Effect#lt;B, E #124; E1, R #124; R1#gt;; #125;
node: Effect.flatMap"/]
+ 38[["type: #40;#41; =#gt; Effect#lt;void, never, never#gt;
node: #40;#41; =#gt; sendRequest"]]
+ 39[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 40[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 41["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 42["type: Effect#lt;void, never, never#gt;
callee: Effect.andThen
args: #91;sendRequest#93;"]
+ 43[/"type: #123; #lt;A, B, E2, R2#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;B, E2, R2#gt;#40;f: Effect#lt;B, E2, R2#gt;#41;: #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #125;
node: Effect.andThen"/]
+ 44[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 45[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 46["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 47[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 48["type: Effect#lt;void, never, never#gt;
callee:
args: #91;#93;"]
+ 49[/"type: #123; #lt;A, B, E2, R2#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;B, E2, R2#gt;#40;f: Effect#lt;B, E2, R2#gt;#41;: #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #125;
node: Effect.andThen"/]
+ 50[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 51[/"type: #quot;100 millis#quot;
node: #quot;100 millis#quot;"/]
+ 52["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 53[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 54["type: Effect#lt;void, never, never#gt;
callee:
args: #91;#93;"]
+ 55[/"type: #123; #lt;A, B, E1, R1#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E1, R #124; R1#gt;; #lt;A, E, R, B, E1, R1#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: Effect#lt;B, E #124; E1, R #124; R1#gt;; #125;
node: Effect.flatMap"/]
+ 56[["type: #40;#41; =#gt; Effect#lt;void, never, never#gt;
node: #40;#41; =#gt; sendRequest"]]
+ 57[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 58[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 59["type: Effect#lt;void, never, never#gt;
callee: Effect.delay
args: #91;#quot;1 second#quot;#93;"]
+ 60[/"type: #123; #40;duration: Input#41;: #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;A, E, R#gt;; #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;, duration: Input#41;: Effect#lt;A, E, R#gt;; #125;
node: Effect.delay"/]
+ 61[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 62[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 63["type: Effect#lt;void, never, never#gt;
callee:
args: #91;#93;"]
+ 64[/"type: #123; #40;duration: Input#41;: #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;A, E, R#gt;; #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;, duration: Input#41;: Effect#lt;A, E, R#gt;; #125;
node: Effect.delay"/]
+ 65[/"type: #quot;100 millis#quot;
node: #quot;100 millis#quot;"/]
+ 66((("type: Effect#lt;void, never, never#gt;
node: Effect.gen#40;function* #40;#41; #123;#92;n yield* Effect.sleep#40;#quot;1 second#quot;#41;#92;n return yield* sendRequest#92;n#125;#41;")))
+ 67[/"type: void
node: yield* sendRequest"/]
+ 68[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 69["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 70[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 71[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 72["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 73[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 74["type: Effect#lt;void, never, never#gt;
callee: withLive
args: #91;#93;"]
+ 75["type: Effect#lt;void, never, never#gt;
callee: Effect.andThen
args: #91;sendRequest#93;"]
+ 76[/"type: #123; #lt;A, B, E2, R2#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;B, E2, R2#gt;#40;f: Effect#lt;B, E2, R2#gt;#41;: #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #125;
node: Effect.andThen"/]
+ 77[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 78[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 79["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 80[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 81["type: Effect#lt;void, never, never#gt;
callee: schedule
args: #91;#93;"]
+ 82["type: Effect#lt;void, never, never#gt;
callee: Effect.andThen
args: #91;sendRequest#93;"]
+ 83[/"type: #123; #lt;A, B, E2, R2#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;B, E2, R2#gt;#40;f: Effect#lt;B, E2, R2#gt;#41;: #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #125;
node: Effect.andThen"/]
+ 84[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 85[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 86["type: #123; pipe: #40;f: any#41; =#gt; any; #125;
callee: unrelatedSleep
args: #91;#93;"]
+ 87[/"type: #40;d: string#41; =#gt; #123; pipe: #40;f: any#41; =#gt; any; #125;
node: unrelatedSleep"/]
+ 88["type:
callee: Effect.andThen
args: #91;sendRequest#93;"]
+ 89[/"type: #123; #lt;A, B, E2, R2#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;B, E2, R2#gt;#40;f: Effect#lt;B, E2, R2#gt;#41;: #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #125;
node: Effect.andThen"/]
+ 90[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 91[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 92["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 93[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 94["type: Effect#lt;void, never, never#gt;
callee: Effect.flatMap
args: #91;#40;val#41; =#gt; Effect.succeed#40;val#41;#93;"]
+ 95[/"type: #123; #lt;A, B, E1, R1#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E1, R #124; R1#gt;; #lt;A, E, R, B, E1, R1#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: Effect#lt;B, E #124; E1, R #124; R1#gt;; #125;
node: Effect.flatMap"/]
+ 96[["type: #40;val: void#41; =#gt; Effect#lt;void, never, never#gt;
node: #40;val#41; =#gt; Effect.succeed#40;val#41;"]]
+ 97[/"type: void
node: val"/]
+ 98["type: Effect#lt;void, never, never#gt;
callee: Effect.succeed
args: #91;#93;"]
+ 99[/"type: #lt;A#gt;#40;value: A#41; =#gt; Effect#lt;A, never, never#gt;
node: Effect.succeed"/]
+ 100[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 101["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 102[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 103["type: Effect#lt;void, never, never#gt;
callee:
args: #91;#93;"]
+ 104[/"type: #123; #lt;A, B, E1, R1#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E1, R #124; R1#gt;; #lt;A, E, R, B, E1, R1#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E1, R1#gt;#41;: Effect#lt;B, E #124; E1, R #124; R1#gt;; #125;
node: Effect.flatMap"/]
+ 105[["type: #40;val: void#41; =#gt; Effect#lt;void, never, never#gt;
node: #40;val#41; =#gt; Effect.succeed#40;val#41;"]]
+ 106[/"type: void
node: val"/]
+ 107["type: Effect#lt;void, never, never#gt;
callee: Effect.succeed
args: #91;#93;"]
+ 108[/"type: #lt;A#gt;#40;value: A#41; =#gt; Effect#lt;A, never, never#gt;
node: Effect.succeed"/]
+ 109[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 110["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 111[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 0 -->|"kind: pipe"| 1
+ 2 -->|"kind: transformCallee"| 1
+ 4 -->|"kind: transformCallee"| 3
+ 5 -->|"kind: transformArg"| 3
+ 1 -->|"kind: pipe"| 3
+ 6 -->|"kind: pipe"| 7
+ 8 -->|"kind: transformCallee"| 7
+ 10 -->|"kind: transformCallee"| 9
+ 12 -->|"kind: potentialReturn"| 11
+ 11 -->|"kind: transformArg"| 9
+ 7 -->|"kind: pipe"| 9
+ 13 -->|"kind: pipe"| 14
+ 15 -->|"kind: transformCallee"| 14
+ 17 -->|"kind: transformCallee"| 16
+ 19 -->|"kind: potentialReturn"| 18
+ 18 -->|"kind: transformArg"| 16
+ 14 -->|"kind: pipe"| 16
+ 20 -->|"kind: pipe"| 21
+ 22 -->|"kind: transformCallee"| 21
+ 24 -->|"kind: transformCallee"| 23
+ 26 -->|"kind: potentialReturn"| 25
+ 25 -->|"kind: transformArg"| 23
+ 21 -->|"kind: pipe"| 23
+ 27 -->|"kind: pipe"| 28
+ 29 -->|"kind: transformCallee"| 28
+ 31 -->|"kind: transformCallee"| 30
+ 32 -->|"kind: transformArg"| 30
+ 28 -->|"kind: pipe"| 30
+ 33 -->|"kind: pipe"| 34
+ 35 -->|"kind: transformCallee"| 34
+ 37 -->|"kind: transformCallee"| 36
+ 39 -->|"kind: potentialReturn"| 38
+ 38 -->|"kind: transformArg"| 36
+ 34 -->|"kind: pipe"| 36
+ 40 -->|"kind: pipe"| 41
+ 43 -->|"kind: transformCallee"| 42
+ 44 -->|"kind: transformArg"| 42
+ 41 -->|"kind: pipe"| 42
+ 45 -->|"kind: pipe"| 46
+ 47 -->|"kind: transformCallee"| 46
+ 46 -->|"kind: pipe"| 48
+ 49 -->|"kind: transformCallee"| 45
+ 50 -->|"kind: transformArg"| 45
+ 51 -->|"kind: pipe"| 52
+ 53 -->|"kind: transformCallee"| 52
+ 52 -->|"kind: pipe"| 54
+ 55 -->|"kind: transformCallee"| 51
+ 57 -->|"kind: potentialReturn"| 56
+ 56 -->|"kind: transformArg"| 51
+ 60 -->|"kind: transformCallee"| 59
+ 61 -->|"kind: transformArg"| 59
+ 58 -->|"kind: pipe"| 59
+ 62 -->|"kind: pipe"| 63
+ 64 -->|"kind: transformCallee"| 62
+ 65 -->|"kind: transformArg"| 62
+ 67 -->|"kind: potentialReturn"| 66
+ 68 -->|"kind: pipe"| 69
+ 70 -->|"kind: transformCallee"| 69
+ 69 -->|"kind: yieldable"| 66
+ 71 -->|"kind: pipe"| 72
+ 73 -->|"kind: transformCallee"| 72
+ 72 -->|"kind: pipe"| 74
+ 76 -->|"kind: transformCallee"| 75
+ 77 -->|"kind: transformArg"| 75
+ 74 -->|"kind: pipe"| 75
+ 78 -->|"kind: pipe"| 79
+ 80 -->|"kind: transformCallee"| 79
+ 79 -->|"kind: pipe"| 81
+ 83 -->|"kind: transformCallee"| 82
+ 84 -->|"kind: transformArg"| 82
+ 81 -->|"kind: pipe"| 82
+ 85 -->|"kind: pipe"| 86
+ 87 -->|"kind: transformCallee"| 86
+ 89 -->|"kind: transformCallee"| 88
+ 90 -->|"kind: transformArg"| 88
+ 86 -->|"kind: pipe"| 88
+ 91 -->|"kind: pipe"| 92
+ 93 -->|"kind: transformCallee"| 92
+ 95 -->|"kind: transformCallee"| 94
+ 97 -->|"kind: pipe"| 98
+ 99 -->|"kind: transformCallee"| 98
+ 98 -->|"kind: potentialReturn"| 96
+ 96 -->|"kind: transformArg"| 94
+ 92 -->|"kind: pipe"| 94
+ 100 -->|"kind: pipe"| 101
+ 102 -->|"kind: transformCallee"| 101
+ 101 -->|"kind: pipe"| 103
+ 104 -->|"kind: transformCallee"| 100
+ 106 -->|"kind: pipe"| 107
+ 108 -->|"kind: transformCallee"| 107
+ 107 -->|"kind: potentialReturn"| 105
+ 105 -->|"kind: transformArg"| 100
+ 109 -->|"kind: pipe"| 110
+ 111 -->|"kind: transformCallee"| 110
\ No newline at end of file
diff --git a/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.flows.txt b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.flows.txt
new file mode 100644
index 00000000..7579d164
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.flows.txt
@@ -0,0 +1 @@
+/.src/sleepThenEffectToDelay.ts -> sleepThenEffectToDelay.flows.sleepThenEffectToDelay.mermaid
diff --git a/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.layers.txt b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.layers.txt
new file mode 100644
index 00000000..eb56d913
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.layers.txt
@@ -0,0 +1 @@
+==== /.src/sleepThenEffectToDelay.ts (0 layer exports) ====
diff --git a/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.pipings.txt b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.pipings.txt
new file mode 100644
index 00000000..0d3acd8a
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.pipings.txt
@@ -0,0 +1,359 @@
+==== /.src/sleepThenEffectToDelay.ts (21 flows) ====
+
+=== Piping Flow ===
+Location: 9:30 - 11:2
+Node: Effect.sleep("1 second").pipe(\n Effect.andThen(sendRequest)\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (2):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: pipeable
+ callee: Effect.andThen
+ args: [sendRequest]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 14:30 - 16:2
+Node: Effect.sleep("1 second").pipe(\n Effect.flatMap(() => sendRequest)\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (2):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: pipeable
+ callee: Effect.flatMap
+ args: [() => sendRequest]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 19:40 - 21:2
+Node: Effect.sleep("1 second").pipe(\n Effect.flatMap((_) => sendRequest)\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (2):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: pipeable
+ callee: Effect.flatMap
+ args: [(_) => sendRequest]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 24:41 - 26:2
+Node: Effect.sleep("1 second").pipe(\n Effect.flatMap((_unused) => sendRequest)\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (2):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: pipeable
+ callee: Effect.flatMap
+ args: [(_unused) => sendRequest]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 29:36 - 32:2
+Node: pipe(\n Effect.sleep("1 second"),\n Effect.andThen(sendRequest)\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (2):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: pipe
+ callee: Effect.andThen
+ args: [sendRequest]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 35:36 - 38:2
+Node: pipe(\n Effect.sleep("1 second"),\n Effect.flatMap(() => sendRequest)\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (2):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: pipe
+ callee: Effect.flatMap
+ args: [() => sendRequest]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 41:32 - 45:2
+Node: pipe(\n "1 second",\n Effect.sleep,\n Effect.andThen(sendRequest)\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (2):
+ [0] kind: pipe
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: pipe
+ callee: Effect.andThen
+ args: [sendRequest]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 48:35 - 51:2
+Node: Effect.andThen(\n Effect.sleep("1 second"),\n sendRequest\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (2):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: dataFirst
+ callee: Effect.andThen
+ args: [sendRequest]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 54:35 - 57:2
+Node: Effect.flatMap(\n Effect.sleep("100 millis"),\n () => sendRequest\n)
+Node Kind: KindCallExpression
+
+Subject: "100 millis"
+Subject Type: "100 millis"
+
+Transformations (2):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: dataFirst
+ callee: Effect.flatMap
+ args: [() => sendRequest]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 60:29 - 60:72
+Node: sendRequest.pipe(Effect.delay("1 second"))
+Node Kind: KindCallExpression
+
+Subject: sendRequest
+Subject Type: Effect
+
+Transformations (1):
+ [0] kind: pipeable
+ callee: Effect.delay
+ args: ["1 second"]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 63:34 - 63:74
+Node: Effect.delay(sendRequest, "100 millis")
+Node Kind: KindCallExpression
+
+Subject: sendRequest
+Subject Type: Effect
+
+Transformations (1):
+ [0] kind: dataFirst
+ callee: Effect.delay
+ args: ["100 millis"]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 66:33 - 69:3
+Node: Effect.gen(function* () {\n yield* Effect.sleep("1 second")\n return yield* sendRequest\n})
+Node Kind: KindCallExpression
+
+Subject: function* () {\n yield* Effect.sleep("1 second")\n return yield* sendRequest\n}
+Subject Type: () => Generator, void, any>
+
+Transformations (1):
+ [0] kind: call
+ callee: Effect.gen
+ args: (constant)
+ outType: Effect
+
+=== Piping Flow ===
+Location: 67:9 - 67:34
+Node: Effect.sleep("1 second")
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (1):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+
+=== Piping Flow ===
+Location: 73:39 - 76:2
+Node: Effect.sleep("1 second").pipe(\n withLive,\n Effect.andThen(sendRequest)\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (3):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: pipeable
+ callee: withLive
+ args: (constant)
+ outType: Effect
+ [2] kind: pipeable
+ callee: Effect.andThen
+ args: [sendRequest]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 80:38 - 83:2
+Node: Effect.sleep("1 second").pipe(\n schedule,\n Effect.andThen(sendRequest)\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (3):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: pipeable
+ callee: schedule
+ args: (constant)
+ outType: Effect
+ [2] kind: pipeable
+ callee: Effect.andThen
+ args: [sendRequest]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 87:34 - 89:2
+Node: unrelatedSleep("1 second").pipe(\n Effect.andThen(sendRequest)\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (2):
+ [0] kind: call
+ callee: unrelatedSleep
+ args: (constant)
+ outType: { pipe: (f: any) => any; }
+ [1] kind: pipeable
+ callee: Effect.andThen
+ args: [sendRequest]
+ outType:
+
+=== Piping Flow ===
+Location: 92:38 - 94:2
+Node: Effect.sleep("1 second").pipe(\n Effect.flatMap((val) => Effect.succeed(val))\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (2):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: pipeable
+ callee: Effect.flatMap
+ args: [(val) => Effect.succeed(val)]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 93:26 - 93:46
+Node: Effect.succeed(val)
+Node Kind: KindCallExpression
+
+Subject: val
+Subject Type: void
+
+Transformations (1):
+ [0] kind: call
+ callee: Effect.succeed
+ args: (constant)
+ outType: Effect
+
+=== Piping Flow ===
+Location: 97:39 - 100:2
+Node: Effect.flatMap(\n Effect.sleep("1 second"),\n (val) => Effect.succeed(val)\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (2):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: dataFirst
+ callee: Effect.flatMap
+ args: [(val) => Effect.succeed(val)]
+ outType: Effect
+
+=== Piping Flow ===
+Location: 99:11 - 99:31
+Node: Effect.succeed(val)
+Node Kind: KindCallExpression
+
+Subject: val
+Subject Type: void
+
+Transformations (1):
+ [0] kind: call
+ callee: Effect.succeed
+ args: (constant)
+ outType: Effect
+
+=== Piping Flow ===
+Location: 104:35 - 104:60
+Node: Effect.sleep("1 second")
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (1):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
diff --git a/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.quickfixes.txt b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.quickfixes.txt
new file mode 100644
index 00000000..436f57af
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay.quickfixes.txt
@@ -0,0 +1,93 @@
+=== Quick Fix Inventory ===
+
+[D1] (10:3-10:17) TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ Fix 0: "Disable sleepThenEffectToDelay for this line"
+ Fix 1: "Disable sleepThenEffectToDelay for entire file"
+
+[D2] (15:3-15:17) TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ Fix 0: "Disable sleepThenEffectToDelay for this line"
+ Fix 1: "Disable sleepThenEffectToDelay for entire file"
+
+[D3] (20:3-20:17) TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ Fix 0: "Disable sleepThenEffectToDelay for this line"
+ Fix 1: "Disable sleepThenEffectToDelay for entire file"
+
+[D4] (25:3-25:17) TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ Fix 0: "Disable sleepThenEffectToDelay for this line"
+ Fix 1: "Disable sleepThenEffectToDelay for entire file"
+
+[D5] (31:3-31:17) TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ Fix 0: "Disable sleepThenEffectToDelay for this line"
+ Fix 1: "Disable sleepThenEffectToDelay for entire file"
+
+[D6] (37:3-37:17) TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ Fix 0: "Disable sleepThenEffectToDelay for this line"
+ Fix 1: "Disable sleepThenEffectToDelay for entire file"
+
+[D7] (44:3-44:17) TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ Fix 0: "Disable sleepThenEffectToDelay for this line"
+ Fix 1: "Disable sleepThenEffectToDelay for entire file"
+
+[D8] (48:36-48:50) TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ Fix 0: "Disable sleepThenEffectToDelay for this line"
+ Fix 1: "Disable sleepThenEffectToDelay for entire file"
+
+[D9] (54:36-54:50) TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ Fix 0: "Disable sleepThenEffectToDelay for this line"
+ Fix 1: "Disable sleepThenEffectToDelay for entire file"
+
+=== Quick Fix Application Results ===
+
+=== [D1] Fix 0: "Disable sleepThenEffectToDelay for this line" ===
+skipped by default
+
+=== [D1] Fix 1: "Disable sleepThenEffectToDelay for entire file" ===
+skipped by default
+
+=== [D2] Fix 0: "Disable sleepThenEffectToDelay for this line" ===
+skipped by default
+
+=== [D2] Fix 1: "Disable sleepThenEffectToDelay for entire file" ===
+skipped by default
+
+=== [D3] Fix 0: "Disable sleepThenEffectToDelay for this line" ===
+skipped by default
+
+=== [D3] Fix 1: "Disable sleepThenEffectToDelay for entire file" ===
+skipped by default
+
+=== [D4] Fix 0: "Disable sleepThenEffectToDelay for this line" ===
+skipped by default
+
+=== [D4] Fix 1: "Disable sleepThenEffectToDelay for entire file" ===
+skipped by default
+
+=== [D5] Fix 0: "Disable sleepThenEffectToDelay for this line" ===
+skipped by default
+
+=== [D5] Fix 1: "Disable sleepThenEffectToDelay for entire file" ===
+skipped by default
+
+=== [D6] Fix 0: "Disable sleepThenEffectToDelay for this line" ===
+skipped by default
+
+=== [D6] Fix 1: "Disable sleepThenEffectToDelay for entire file" ===
+skipped by default
+
+=== [D7] Fix 0: "Disable sleepThenEffectToDelay for this line" ===
+skipped by default
+
+=== [D7] Fix 1: "Disable sleepThenEffectToDelay for entire file" ===
+skipped by default
+
+=== [D8] Fix 0: "Disable sleepThenEffectToDelay for this line" ===
+skipped by default
+
+=== [D8] Fix 1: "Disable sleepThenEffectToDelay for entire file" ===
+skipped by default
+
+=== [D9] Fix 0: "Disable sleepThenEffectToDelay for this line" ===
+skipped by default
+
+=== [D9] Fix 1: "Disable sleepThenEffectToDelay for entire file" ===
+skipped by default
diff --git a/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.errors.txt b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.errors.txt
new file mode 100644
index 00000000..eb03ab3d
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.errors.txt
@@ -0,0 +1,20 @@
+=== Metadata ===
+Effect version: 4.0.0
+
+/.src/sleepThenEffectToDelay_preview.ts(9,3): suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+
+
+==== /.src/sleepThenEffectToDelay_preview.ts (1 errors) ====
+ // @effect-v4
+ // @effect-diagnostics *:off
+ // @effect-diagnostics sleepThenEffectToDelay:suggestion
+ import { Effect } from "effect"
+
+ declare const sendRequest: Effect.Effect
+
+ export const throttled = Effect.sleep("1 second").pipe(
+ Effect.andThen(sendRequest)
+ ~~~~~~~~~~~~~~
+!!! suggestion TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ )
+
diff --git a/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.flows.sleepThenEffectToDelay_preview.mermaid b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.flows.sleepThenEffectToDelay_preview.mermaid
new file mode 100644
index 00000000..a54d7276
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.flows.sleepThenEffectToDelay_preview.mermaid
@@ -0,0 +1,12 @@
+flowchart TB
+ 0[/"type: #quot;1 second#quot;
node: #quot;1 second#quot;"/]
+ 1["type: Effect#lt;void, never, never#gt;
callee: Effect.sleep
args: #91;#93;"]
+ 2[/"type: #40;duration: Input#41; =#gt; Effect#lt;void, never, never#gt;
node: Effect.sleep"/]
+ 3["type: Effect#lt;void, never, never#gt;
callee: Effect.andThen
args: #91;sendRequest#93;"]
+ 4[/"type: #123; #lt;A, B, E2, R2#gt;#40;f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: #lt;E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;B, E2, R2#gt;#40;f: Effect#lt;B, E2, R2#gt;#41;: #lt;A, E, R#gt;#40;self: Effect#lt;A, E, R#gt;#41; =#gt; Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: #40;a: A#41; =#gt; Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #lt;A, E, R, B, E2, R2#gt;#40;self: Effect#lt;A, E, R#gt;, f: Effect#lt;B, E2, R2#gt;#41;: Effect#lt;B, E #124; E2, R #124; R2#gt;; #125;
node: Effect.andThen"/]
+ 5[/"type: Effect#lt;void, never, never#gt;
node: sendRequest"/]
+ 0 -->|"kind: pipe"| 1
+ 2 -->|"kind: transformCallee"| 1
+ 4 -->|"kind: transformCallee"| 3
+ 5 -->|"kind: transformArg"| 3
+ 1 -->|"kind: pipe"| 3
\ No newline at end of file
diff --git a/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.flows.txt b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.flows.txt
new file mode 100644
index 00000000..2779189f
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.flows.txt
@@ -0,0 +1 @@
+/.src/sleepThenEffectToDelay_preview.ts -> sleepThenEffectToDelay_preview.flows.sleepThenEffectToDelay_preview.mermaid
diff --git a/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.layers.txt b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.layers.txt
new file mode 100644
index 00000000..68080cf0
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.layers.txt
@@ -0,0 +1 @@
+==== /.src/sleepThenEffectToDelay_preview.ts (0 layer exports) ====
diff --git a/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.pipings.txt b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.pipings.txt
new file mode 100644
index 00000000..430f5740
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.pipings.txt
@@ -0,0 +1,19 @@
+==== /.src/sleepThenEffectToDelay_preview.ts (1 flows) ====
+
+=== Piping Flow ===
+Location: 8:25 - 10:2
+Node: Effect.sleep("1 second").pipe(\n Effect.andThen(sendRequest)\n)
+Node Kind: KindCallExpression
+
+Subject: "1 second"
+Subject Type: "1 second"
+
+Transformations (2):
+ [0] kind: call
+ callee: Effect.sleep
+ args: (constant)
+ outType: Effect
+ [1] kind: pipeable
+ callee: Effect.andThen
+ args: [sendRequest]
+ outType: Effect
diff --git a/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.quickfixes.txt b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.quickfixes.txt
new file mode 100644
index 00000000..8f821763
--- /dev/null
+++ b/testdata/baselines/reference/effect-v4/sleepThenEffectToDelay_preview.quickfixes.txt
@@ -0,0 +1,13 @@
+=== Quick Fix Inventory ===
+
+[D1] (9:3-9:17) TS377133: Sequencing Effect.sleep before an effect re-implements Effect.delay. effect(sleepThenEffectToDelay)
+ Fix 0: "Disable sleepThenEffectToDelay for this line"
+ Fix 1: "Disable sleepThenEffectToDelay for entire file"
+
+=== Quick Fix Application Results ===
+
+=== [D1] Fix 0: "Disable sleepThenEffectToDelay for this line" ===
+skipped by default
+
+=== [D1] Fix 1: "Disable sleepThenEffectToDelay for entire file" ===
+skipped by default
diff --git a/testdata/tests/effect-v3/sleepThenEffectToDelay.ts b/testdata/tests/effect-v3/sleepThenEffectToDelay.ts
new file mode 100644
index 00000000..d2094264
--- /dev/null
+++ b/testdata/tests/effect-v3/sleepThenEffectToDelay.ts
@@ -0,0 +1,19 @@
+// @effect-v3
+// @effect-diagnostics *:off
+// @effect-diagnostics sleepThenEffectToDelay:suggestion
+import { Effect } from "effect"
+
+declare const sendRequest: Effect.Effect
+
+// Negative control: GOOD forms only
+export const goodDelayPipe = sendRequest.pipe(Effect.delay("1 second"))
+export const goodDelayDataFirst = Effect.delay(sendRequest, "100 millis")
+
+export const goodGenStandalone = Effect.gen(function* () {
+ yield* Effect.sleep("1 second")
+ return yield* sendRequest
+})
+
+export const goodParamUsingCallback = Effect.sleep("1 second").pipe(
+ Effect.flatMap((val) => Effect.succeed(val))
+)
diff --git a/testdata/tests/effect-v4/sleepThenEffectToDelay.ts b/testdata/tests/effect-v4/sleepThenEffectToDelay.ts
new file mode 100644
index 00000000..0ea7139d
--- /dev/null
+++ b/testdata/tests/effect-v4/sleepThenEffectToDelay.ts
@@ -0,0 +1,104 @@
+// @effect-v4
+// @effect-diagnostics *:off
+// @effect-diagnostics sleepThenEffectToDelay:suggestion
+import { Effect, pipe } from "effect"
+
+declare const sendRequest: Effect.Effect
+
+// BAD: pipe form with andThen
+export const badPipeAndThen = Effect.sleep("1 second").pipe(
+ Effect.andThen(sendRequest)
+)
+
+// BAD: pipe form with flatMap (0 params)
+export const badPipeFlatMap = Effect.sleep("1 second").pipe(
+ Effect.flatMap(() => sendRequest)
+)
+
+// BAD: pipe form with flatMap (unused param _)
+export const badPipeFlatMapUnderscore = Effect.sleep("1 second").pipe(
+ Effect.flatMap((_) => sendRequest)
+)
+
+// BAD: pipe form with flatMap (unused named param)
+export const badPipeFlatMapUnusedNamed = Effect.sleep("1 second").pipe(
+ Effect.flatMap((_unused) => sendRequest)
+)
+
+// BAD: pipe helper function with andThen
+export const badPipeHelperAndThen = pipe(
+ Effect.sleep("1 second"),
+ Effect.andThen(sendRequest)
+)
+
+// BAD: pipe helper function with flatMap
+export const badPipeHelperFlatMap = pipe(
+ Effect.sleep("1 second"),
+ Effect.flatMap(() => sendRequest)
+)
+
+// BAD: pipe with sleep transformation step (flow.Transformations[0])
+export const badPipeSleepStep = pipe(
+ "1 second",
+ Effect.sleep,
+ Effect.andThen(sendRequest)
+)
+
+// BAD: data-first andThen
+export const badDataFirstAndThen = Effect.andThen(
+ Effect.sleep("1 second"),
+ sendRequest
+)
+
+// BAD: data-first flatMap
+export const badDataFirstFlatMap = Effect.flatMap(
+ Effect.sleep("100 millis"),
+ () => sendRequest
+)
+
+// GOOD: Effect.delay in pipe form
+export const goodDelayPipe = sendRequest.pipe(Effect.delay("1 second"))
+
+// GOOD: Effect.delay data-first form
+export const goodDelayDataFirst = Effect.delay(sendRequest, "100 millis")
+
+// GOOD: standalone yield* sleep in Effect.gen
+export const goodGenStandalone = Effect.gen(function* () {
+ yield* Effect.sleep("1 second")
+ return yield* sendRequest
+})
+
+// GOOD: non-sequencer first stage (withLive)
+declare const withLive: (effect: Effect.Effect) => Effect.Effect
+export const goodTestClockFirstStage = Effect.sleep("1 second").pipe(
+ withLive,
+ Effect.andThen(sendRequest)
+)
+
+// GOOD: non-sequencer first stage (schedule)
+declare const schedule: (effect: Effect.Effect) => Effect.Effect
+export const goodScheduleFirstStage = Effect.sleep("1 second").pipe(
+ schedule,
+ Effect.andThen(sendRequest)
+)
+
+// GOOD: unrelated sleep function
+declare const unrelatedSleep: (d: string) => { pipe: (f: any) => any }
+export const goodUnrelatedSleep = unrelatedSleep("1 second").pipe(
+ Effect.andThen(sendRequest)
+)
+
+// GOOD: param-using callback in pipe flatMap
+export const goodParamUsingCallback = Effect.sleep("1 second").pipe(
+ Effect.flatMap((val) => Effect.succeed(val))
+)
+
+// GOOD: param-using callback in data-first flatMap
+export const goodDataFirstParamUsing = Effect.flatMap(
+ Effect.sleep("1 second"),
+ (val) => Effect.succeed(val)
+)
+
+
+// GOOD: standalone sleep without sequencer
+export const goodStandaloneSleep = Effect.sleep("1 second")
diff --git a/testdata/tests/effect-v4/sleepThenEffectToDelay_preview.ts b/testdata/tests/effect-v4/sleepThenEffectToDelay_preview.ts
new file mode 100644
index 00000000..d0de7af8
--- /dev/null
+++ b/testdata/tests/effect-v4/sleepThenEffectToDelay_preview.ts
@@ -0,0 +1,10 @@
+// @effect-v4
+// @effect-diagnostics *:off
+// @effect-diagnostics sleepThenEffectToDelay:suggestion
+import { Effect } from "effect"
+
+declare const sendRequest: Effect.Effect
+
+export const throttled = Effect.sleep("1 second").pipe(
+ Effect.andThen(sendRequest)
+)