<!-- Generated by gzkit — do not edit manually -->
# src Agent Instructions

> Shared subtree rules. See root `AGENTS.md` for project-wide governance.

# Cross-Platform Development Policy (Binding)

**Platforms:** Windows (primary), macOS, Linux
**Doctrine:** ADR-0.0.1

---

## Quick Reference

| Category     | Use                             | Avoid                         |
| ------------ | ------------------------------- | ----------------------------- |
| Paths        | `Path("dir") / "file"`          | `"dir/file"` or `"dir\\file"` |
| Encoding     | `encoding="utf-8"`              | Default encoding              |
| Temp files   | Context managers                | Raw `shutil.rmtree()`         |
| Subprocess   | List form, `uv run`             | `shell=True`, bare `python`   |
| Cleanup      | Context managers, `gc.collect()` | `ignore_errors=True`          |
| Line endings | `newline=""` for CSV            | Hard-coded `\r\n`             |

---

## File Paths (Always pathlib.Path)

```python
from pathlib import Path
config_path = Path("config") / "settings.json"
config_path.read_text(encoding="utf-8")
```

---

## Encoding (Always UTF-8)

```python
Path("data.json").read_text(encoding="utf-8")
```

---

## Temporary Files (Context Managers)

```python
with tempfile.TemporaryDirectory() as temp_dir:
    temp_path = Path(temp_dir) / "data.json"
    # Cleanup automatic (Windows-safe)
```

---

## Subprocess (List Form)

```python
subprocess.run(["git", "status"], capture_output=True, text=True, encoding="utf-8")
subprocess.run(["uv", "run", "-m", "unittest"], check=True)
```

---

## Code Review Checklist

- [ ] All file operations use `pathlib.Path`
- [ ] All file I/O specifies `encoding="utf-8"`
- [ ] Temp files use context managers
- [ ] No `shell=True` in subprocess
- [ ] No hard-coded path separators

# Data Model Policy (canonical)

- Use **Pydantic `BaseModel`** for all data models; no stdlib `dataclasses`.
- Use `ConfigDict(frozen=True, extra="forbid")` for immutable models.
- Use `Field(...)` with descriptions for required fields; `Field(None, ...)` for optional.
- Use type hints (`str | None`, `list[str]`) — not `Optional`, `List`.

## Why Pydantic Over Dataclasses

| Feature | Pydantic | dataclasses |
|---------|----------|-------------|
| Validation | Built-in, declarative | Manual |
| Serialization | `.model_dump()`, `.model_dump_json()` | Manual |
| Immutability | `frozen=True` with clear errors | `frozen=True` with cryptic errors |
| Extra fields | `extra="forbid"` rejects typos | Silent ignore or manual check |

## Pattern: Immutable Domain Model

```python
from pydantic import BaseModel, ConfigDict, Field

class WorldState(BaseModel):
    model_config = ConfigDict(frozen=True, extra="forbid")

    world_state_hash: str = Field(..., description="SHA-256 of stable content")
    contract_hash: str | None = Field(None, description="Contract fingerprint hash")
```

## Anti-Patterns (DO NOT USE)

- stdlib `dataclass` for governance data
- Pydantic without `ConfigDict`
- `Optional`/`List` instead of `| None` and `list[]`

## Verify

- All models extend `BaseModel` (not `@dataclass`)
- All models have `model_config` with at least `extra="forbid"`
- Immutable snapshots use `frozen=True`
