dfrotz prompt detection — reading output from a z-machine subprocess ==================================================================== the problem =========== dfrotz writes game text to stdout, then displays a ">" prompt and waits for input. there's no clean "end of response" delimiter — just the prompt character appearing at the end of the output. this sounds simple. it wasn't. what made it hard ================= 1. the prompt format varies: - "> " (with trailing space) — most common - ">\n" or ">\r\n" — sometimes - bare ">" with no preceding newline — edge case but real 2. the ">" character can appear IN game text: - a bare ">" at end of text isn't always the prompt - only strip it when the raw bytes show trailing whitespace after it (confirming it's the prompt pattern, not game content) 3. chunked I/O: - data arrives in arbitrary chunks (not line-by-line) - the prompt might be split across chunks - can't just check the last character of each read 4. timing: - after writing a command, dfrotz takes variable time to respond - short responses (one line) arrive fast - long responses (room descriptions) come in multiple chunks - no way to know in advance how much output to expect what we do ========== _read_response() in if_session.py uses a chunked reader with dual checks: read up to 1024 bytes per chunk after each chunk, check the accumulated buffer for prompt patterns idle timeout of 100ms — once data stops flowing, return what we have overall deadline of 5 seconds — safety net prompt detection checks (in order): 1. stripped text ends with "\n>" — always a prompt, strip it 2. stripped text is just ">" — empty response, return "" 3. raw bytes end with "> " or ">\n" AND stripped text ends with ">" — prompt confirmed by trailing whitespace, strip it the key insight: "\n>" is unambiguously a prompt (game text doesn't end that way). bare ">" is ambiguous — only treat it as a prompt when the raw bytes have trailing whitespace confirming the prompt pattern. what we tried that didn't work ============================== - reading until timeout only: too slow (100ms wait after every response) or too fast (miss data that's still coming). need both prompt detection AND timeout as fallback. - reading line by line: dfrotz doesn't always end the prompt with a newline. sometimes the ">" has a trailing space and nothing else. readline() hangs waiting for \n that never comes. - checking only the last character: ">" appears in game text. false positives everywhere. the fix pattern =============== dual-mode detection: fast path checks for prompt patterns after each chunk (instant return when found), slow path falls back to idle timeout (catches edge cases where prompt detection fails). overall deadline prevents infinite hangs. this pattern works for any subprocess that uses a prompt character to signal readiness. the specific prompt patterns are dfrotz-specific, but the chunk-accumulate-check loop is reusable.