diff --git a/.trae/rules/project_rules.md b/.trae/rules/project_rules.md new file mode 100644 index 0000000..b749419 --- /dev/null +++ b/.trae/rules/project_rules.md @@ -0,0 +1,39 @@ +# Project Rules + +## Python Async Generator Safety + +When writing async generator functions (async def with yield), NEVER use early `return` before the first `yield`. This causes Python to treat the function as a coroutine instead of an async generator, resulting in `'async for' requires an object with __aiter__ method, got coroutine` errors at runtime. + +**Wrong:** +```python +async def subscribe(self): + if self._closed: + return # BUG: makes this a coroutine when _closed=True + ... + yield event +``` + +**Correct — use `return; yield` pattern:** +```python +async def subscribe(self): + if self._closed: + return + yield # Makes this always an async generator + ... + yield event +``` + +**Correct — restructure to avoid early return:** +```python +async def subscribe(self): + if not self._closed: + ... + yield event +``` + +This applies to ALL async generator functions in the codebase. When adding an early exit path to any `async def` that contains `yield`, always ensure the `yield` is reachable or add a `return; yield` guard. + +## Testing + +- Run `python3 -m pytest tests/unit/ -x -q` before committing +- Known failing test (unrelated): `test_rewoo_agent_yaml_loads` — skip if needed diff --git a/AGENTS.md b/AGENTS.md index dac2acc..383ae87 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,6 +10,7 @@ - Expert names validated with `_EXPERT_NAME_RE = re.compile(r"^[a-zA-Z0-9_-]{1,64}$")` - HandoffTransport queues bounded (`maxsize=1024`), close uses sentinel pattern - Frontend: Vue 3 + TypeScript + Ant Design Vue, Pinia stores, no `require()` calls +- **Async generator safety**: Never use early `return` before the first `yield` in `async def` — use `return; yield` pattern instead (see `.trae/rules/project_rules.md`) ## Tech Stack