You are to implement a Trie (prefix tree) and a scenario runner function. The Trie class must support inserting words and searching for exact words or prefixes. The class must have the following methods:
- `__init__(self)`: initializes an empty trie.
- `insert(self, word: str) -> None`: inserts the given word into the trie. Returns nothing.
- `search(self, word: str) -> bool`: returns `True` if the exact word has been inserted previously, otherwise `False`.
- `starts_with(self, prefix: str) -> bool`: returns `True` if there is any inserted word that has the given prefix, otherwise `False`.
Additionally, implement a function `run_trie_scenario(operations: list[list]) -> list` that takes a list of operations, each operation being `[op_name, argument]` or `[op_name]` (e.g., `["search", "apple"]`, `["insert", "apple"]`). For each operation, if op_name is `"insert"`, perform the insertion and append `None` to the result. For `"search"` and `"starts_with"`, append the corresponding boolean result. The function must use a single `Trie` instance created at the start.
All words consist of lowercase English letters only. The methods should be case-sensitive. Implement both the `Trie` class and the `run_trie_scenario` function in Python. Example usage:
```python
trie = Trie()
trie.insert("apple")
trie.search("apple") # returns True
trie.search("app") # returns False
trie.starts_with("app") # returns True
trie.insert("app")
trie.search("app") # returns True
run_trie_scenario([["insert", "apple"], ["search", "apple"], ["search", "app"], ["starts_with", "app"], ["insert", "app"], ["search", "app"]])
# returns [None, True, False, True, None, True]
```
Constraints
- `1 <= len(word) <= 2000` for any word or prefix.
- The total number of operations across all calls to `run_trie_scenario` is at most `10^4`.
- All inputs consist of lowercase English letters only.
- Your implementation should aim for O(L) time per operation, where L is the length of the word/prefix, and O(total inserted characters) space.