--- name: senior-python-engineering description: "The standard for writing Python here — small, typed, explicit, testable modules with side effects pushed to the edges. Use when implementing or designing any Python code." generated: true source: .ai/skills/senior-python-engineering.md --- # Skill: Senior Python Engineering ## Purpose Set the standard for writing Python in this repository: small, typed, explicit, testable modules with side effects pushed to the edges — code that future agents can safely modify without fear. ## When to Use This Skill - Implementing any Python code in `src/agentplatform/` (tasks `0001`+). - Designing a new module, model, or adapter. - Deciding between dataclass vs. Pydantic, sync vs. async, or where to put a dependency. ## Files to Read First - `AGENTS.md` (architecture rules and planned package layout). - `docs/ARCHITECTURE.md` (layer responsibilities). - `.ai/skills/architecture-review.md` (invariants you must not break). - `pyproject.toml` (configured tools: ruff, mypy, pytest). ## Core Principles - **Small modules, single responsibility.** One layer concern per module; no giant files. - **Explicit typed models.** Type everything; `mypy` runs strict. - Use **Pydantic** for data crossing a boundary that needs validation/parsing (YAML spec models, plugin/API payloads). - Use **dataclasses** (often frozen) for internal domain models that don't need runtime validation (e.g. compiled graph nodes). Choose intentionally, not at random. - **Separate domain models from transport/API models.** Don't reuse a FastAPI request model as your internal domain object. - **Keep side effects at boundaries.** I/O, network, time, and randomness live in adapters at the edges; core logic stays pure and testable. - **Dependency injection over hidden globals.** Pass collaborators in; no module-level mutable singletons, and never mutable global request state. - **Adapters via explicit interfaces.** External integrations (LLM, MCP, DB, plugins) sit behind ABCs so implementations are navigable and can be faked in tests. Reach for `typing.Protocol` where the typing is structural rather than nominal: `runtime_checkable` Protocols for *optional* capabilities detected with `isinstance` (see `agent_engine/engine/`), plain Protocols for the minimal shape a function needs of its argument. - **Clear async boundaries.** Don't mix blocking I/O into async paths; keep async at the edges and be consistent within a module. - **Typed, actionable errors.** Define specific exception types; messages name the offending thing (key, id, variable). Avoid bare `except`. - **Small, intent-named functions.** A function name should describe what it does; if you need "and" in the name, split it. - **Avoid clever code and premature abstraction.** Write the simple version; abstract only when a second real case appears. ## Process 1. **Locate the layer.** Put code in the correct `src/agentplatform/` package per `AGENTS.md`. 2. **Model the data.** Decide Pydantic (boundary/validated) vs. dataclass (internal/domain); make domain models immutable where possible. 3. **Define the interface first.** Sketch the public function/class signatures and any ABC for external dependencies — or a `runtime_checkable` Protocol when the capability is optional and detected structurally at runtime. 4. **Implement the core pure logic**, keeping I/O behind injected adapters. 5. **Wire dependencies explicitly** (constructor/params), not via globals. 6. **Type and lint:** run `make format` then `make lint` (ruff + mypy). 7. **Add tests** per `.ai/skills/testing.md`; run `make check`. ## Expected tools - `pytest` — tests. - `ruff` — formatting + linting. - `mypy` (or `pyright` if later configured) — static typing. - `pydantic` — boundary/validated models. - `fastapi` — when the API layer is implemented (task `0009`). - `typer` — when the CLI is implemented (task `0008`). ## Checklist Before Finishing - [ ] Code is in the correct layer; modules are small and single-purpose. - [ ] Everything is typed; `mypy` is clean. - [ ] Pydantic vs. dataclass chosen intentionally and justified. - [ ] Domain models separated from transport/API models. - [ ] Side effects isolated at adapters; core logic is pure. - [ ] Dependencies injected; no hidden/mutable globals; no global request state. - [ ] External integrations sit behind explicit ABCs; optional capabilities behind `runtime_checkable` Protocols. - [ ] Errors are typed and actionable. - [ ] No premature abstraction or clever code. - [ ] `make check` passes. ## Common Mistakes to Avoid - One big module that spans several layers. - Untyped code or `Any` used to silence `mypy`. - Reusing transport models as domain models (or vice versa). - Hidden global state / module-level singletons holding request data. - Concrete external clients hardwired in core logic (untestable). - Building frameworks/abstractions before there's a second use case. ## Expected Final Report State: which modules were added/changed and the layer each belongs to; the model choices (Pydantic vs. dataclass) and why; how external dependencies are injected and faked; confirmation that side effects are at the edges; and the `make check` result.