[WIP] Create design document for K2 REPL
^KT-82360
diff --git a/docs/fir/fir-repl.md b/docs/fir/fir-repl.md
new file mode 100644
index 0000000..ebce032
--- /dev/null
+++ b/docs/fir/fir-repl.md
@@ -0,0 +1,138 @@
+# FIR REPL Snippets
+
+## Conversion
+
+Kotlin REPL snippets are converted to `object`s during compilation.
+This is conversion is performed during parsing.
+Declarations are extracted to exist at the class level while statements are moved to the body of a function: `$$eval`.
+To make sure resolution happens in the correct order,
+the body of the `$$eval` function is resolved first and each declaration is resolved as it originally appeared in the snippet.
+
+### Snippet
+
+```kotlin
+// SNIPPET
+data class Foo(val x: Int)
+private fun Foo.plusOne() = Foo(x + 1)
+val x = Foo(1)
+println(x.plusOne())
+```
+
+### Transformed
+
+```kotlin
+object Snippet {
+ fun `$$eval`() {
+ // <jump to resolve Foo>
+ // <jump to resolve plusOne>
+ // <jump to resolve x>
+ println(x.plusOne())
+ }
+
+ data class Foo(val x: Int)
+ private fun Foo.plusOne() = Foo(x + 1)
+ val x = Foo(1)
+}
+```
+
+## Type Resolution
+
+To resolve classifiers from previous snippets, the `FirReplSnippetResolveExtension` provides an `FirScope` for all previous snippet declarations.
+This scope is added during `SUPER_TYPES`, `TYPES`, and `BODY_RESOLVE` phases.
+
+## Body Resolution
+
+To achieve the resolution jump from the `$$eval` function, a new `FirStatement` node was introduced called `FirReplDeclarationReference`.
+This new node holds a symbol for the class-level declaration it references.
+Declarations moved to the class-level are tagged with the `isReplSnippetDeclaration` attribute.
+
+During resolution, when a `FirReplDeclarationReference` is encountered, the referenced declaration is immediately resolved.
+By inserting a `FirReplDeclarationReference` node in the `$$eval` function body where each declaration originally appeared in the snippet,
+this preserves the control-flow graph of the snippet.
+
+It is imperative that the `$$eval` function be resolved first.
+This is done naturally by the compiler by placing it as the first declaration in the transformed object.
+
+### Control-Flow / Data-Flow
+
+Changes to the control-flow graph are required to treat what are technically class-level properties as local properties for smart-casting.
+Because of how body resolution jumps to each declaration, the control-flow graph is constructed correctly.
+However, since properties inferred to anonymous types need to be approximated, this requires smart-casting after property initialization.
+
+[//]: # (TODO - there probably more to be said here, but not sure what details are important)
+
+### Candidates
+
+It is possible to reference declarations defined in previous snippets.
+It is also possible to override declarations from previous snippets with new declarations.
+
+```kotlin
+// SNIPPET
+val x = 1
+fun foo(): String = "foo"
+
+// SNIPPET
+val x = 2
+fun foo(): String = "bar"
+
+// SNIPPET
+println(foo() + x)
+```
+
+`FirReplSnippetResolveExtension` is what provides access to an `FirScope` for all previous snippets.
+This will walk the REPL history and add class-level declarations to a custom scope.
+The `FirScope` for previous snippets is a separate tower level from the current snippet.
+This means that declarations from the current snippet will always take precedence over declarations from previous snippets,
+even if declarations from previous snippets have more specific types.
+
+```kotlin
+// SNIPPET
+fun foo(x: Any): Int = 1
+fun foo(x: Int): Int = 2
+
+foo(0) // 2
+
+// SNIPPET
+fun foo(x: Number): Int = 3
+
+foo(0) // 3
+```
+
+`ReplOverloadCallConflictResolver` is what limits callable candidates to only the most recent snippet.
+This is what allows repeatedly defining declarations with the same name without conflicts.
+Declarations from previous snippets are tagged with the `originalReplSnippetSymbol` attribute.
+
+## Future: Suspend
+
+In the future, to support suspend function calls in REPL snippets, property initializers will need to structurally exist within the `$$eval`
+function body.
+This will hopefully avoid any changes to FIR checkers which validation the ability to call a suspend function from the correct scope.
+
+[//]: # (TODO)
+
+### Snippet
+
+```kotlin
+// SNIPPET
+data class Foo(val x: Int)
+private suspend fun Foo.plusOne() = Foo(x + 1)
+val x = Foo(1).plusOne()
+println(x)
+```
+
+### Transformed
+
+```kotlin
+object Snippet {
+ fun `$$eval`() {
+ // <jump to resolve Foo>
+ // <jump to resolve plusOne>
+ x = Foo(1).plusOne() // <repl property initializer>
+ println(x)
+ }
+
+ data class Foo(val x: Int)
+ private suspend fun Foo.plusOne() = Foo(x + 1)
+ val x // = <expression reference>
+}
+```
\ No newline at end of file