Private
Public Access
feat(directives): harvest 7 directives from python.md §17.1-17.7 (banned patterns + boundary exception)
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
# ban_any_type — v1
|
||||
|
||||
**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.2 (lines 267-277).
|
||||
This is the baseline encoding — the imperative-ban style currently in production.
|
||||
Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline.
|
||||
|
||||
**Source:** `conductor/code_styleguides/python.md:267-277`
|
||||
|
||||
---
|
||||
|
||||
### 17.2 Banned: `Any`
|
||||
|
||||
```python
|
||||
# BANNED:
|
||||
def _to_typed_tool_call(tc: Any) -> ToolCall:
|
||||
return ToolCall(id=getattr(tc, "id", "") or "", ...)
|
||||
|
||||
# CORRECT:
|
||||
def _parse_wire_tool_call(wire: dict[str, Any]) -> ToolCall:
|
||||
"""Boundary: parse MCP wire dict to typed ToolCall."""
|
||||
return ToolCall.from_dict(wire)
|
||||
```
|
||||
@@ -0,0 +1,28 @@
|
||||
# ban_dict_any — v1
|
||||
|
||||
**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.1 (lines 247-265).
|
||||
This is the baseline encoding — the imperative-ban style currently in production.
|
||||
Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline.
|
||||
|
||||
**Source:** `conductor/code_styleguides/python.md:247-265`
|
||||
|
||||
---
|
||||
|
||||
### 17.1 Banned: `dict[str, Any]`
|
||||
|
||||
```python
|
||||
# BANNED:
|
||||
def process(event: dict[str, Any]) -> None:
|
||||
if event.get("kind") == "tool_call":
|
||||
|
||||
# BANNED:
|
||||
flat: dict[str, Any] = project_manager.flat_config(...)
|
||||
|
||||
# CORRECT:
|
||||
def process(event: CommsLogEntry) -> None:
|
||||
if event.kind == "tool_call":
|
||||
|
||||
# CORRECT (boundary only):
|
||||
def _parse_wire(raw: str) -> Metadata:
|
||||
return Metadata.from_dict(tomllib.loads(raw))
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
# ban_dict_get_on_known_fields — v1
|
||||
|
||||
**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.6 (lines 340-350).
|
||||
This is the baseline encoding — the imperative-ban style currently in production.
|
||||
Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline.
|
||||
|
||||
**Source:** `conductor/code_styleguides/python.md:340-350`
|
||||
|
||||
---
|
||||
|
||||
### 17.6 Banned: `.get('field', default)` on a `dict[str, Any]`
|
||||
|
||||
```python
|
||||
# BANNED:
|
||||
tier = entry.get('source_tier', 'main')
|
||||
model = entry.get('model', 'unknown')
|
||||
|
||||
# CORRECT (direct attribute access on the typed dataclass):
|
||||
tier = entry.source_tier
|
||||
model = entry.model
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
# ban_getattr_dispatch — v1
|
||||
|
||||
**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.5 (lines 328-338).
|
||||
This is the baseline encoding — the imperative-ban style currently in production.
|
||||
Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline.
|
||||
|
||||
**Source:** `conductor/code_styleguides/python.md:328-338`
|
||||
|
||||
---
|
||||
|
||||
### 17.5 Banned: `getattr(x, 'field', default)` for type dispatch
|
||||
|
||||
```python
|
||||
# BANNED:
|
||||
tool_id = getattr(tc, "id", "") or ""
|
||||
tool_name = getattr(tc.function, "name", "") or ""
|
||||
|
||||
# CORRECT:
|
||||
tool_id = tc.id
|
||||
tool_name = tc.function.name
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
# ban_hasattr_dispatch — v1
|
||||
|
||||
**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.4 (lines 300-326).
|
||||
This is the baseline encoding — the imperative-ban style currently in production.
|
||||
Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline.
|
||||
|
||||
**Source:** `conductor/code_styleguides/python.md:300-326`
|
||||
|
||||
---
|
||||
|
||||
### 17.4 Banned: `hasattr()` for entity type dispatch
|
||||
|
||||
```python
|
||||
# BANNED:
|
||||
def handle_event(self, event: Metadata) -> None:
|
||||
if hasattr(event, 'tool_calls'):
|
||||
# tool call path
|
||||
elif hasattr(event, 'source_tier'):
|
||||
# mma path
|
||||
elif hasattr(event, 'path'):
|
||||
# file path
|
||||
|
||||
# CORRECT (typed Union dispatch):
|
||||
def handle_event(self, event: CommsLogEntry | FileItem | HistoryMessage) -> None:
|
||||
if isinstance(event, CommsLogEntry):
|
||||
# mma path
|
||||
elif isinstance(event, FileItem):
|
||||
# file path
|
||||
elif isinstance(event, HistoryMessage):
|
||||
# tool call path
|
||||
|
||||
# CORRECT (preferred — refactor so no dispatch is needed):
|
||||
def _handle_comms_entry(self, event: CommsLogEntry) -> None: ...
|
||||
def _handle_file_item(self, event: FileItem) -> None: ...
|
||||
def _handle_history(self, event: HistoryMessage) -> None: ...
|
||||
```
|
||||
@@ -0,0 +1,31 @@
|
||||
# ban_optional_returns — v1
|
||||
|
||||
**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.3 (lines 279-298).
|
||||
This is the baseline encoding — the imperative-ban style currently in production.
|
||||
Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline.
|
||||
|
||||
**Source:** `conductor/code_styleguides/python.md:279-298`
|
||||
|
||||
---
|
||||
|
||||
### 17.3 Banned: `Optional[T]` returns
|
||||
|
||||
```python
|
||||
# BANNED:
|
||||
def find_ticket(self, id: str) -> Optional[Ticket]:
|
||||
for t in self.active_tickets:
|
||||
if t.id == id: return t
|
||||
return None # ← silent failure; consumer has to None-check
|
||||
|
||||
# CORRECT (Result pattern):
|
||||
def find_ticket(self, id: str) -> Result[Ticket]:
|
||||
for t in self.active_tickets:
|
||||
if t.id == id: return Result(data=t)
|
||||
return Result(data=NIL_TICKET, errors=[ErrorInfo(...)]) # drain point handles
|
||||
|
||||
# CORRECT (NIL_T sentinel — preferred when consumer just reads fields):
|
||||
def find_ticket(self, id: str) -> Ticket:
|
||||
for t in self.active_tickets:
|
||||
if t.id == id: return t
|
||||
return NIL_TICKET # zero-initialized frozen dataclass; safe to read fields
|
||||
```
|
||||
@@ -0,0 +1,13 @@
|
||||
# boundary_layer_exception — v1
|
||||
|
||||
**Why this iteration:** Lifted verbatim from `conductor/code_styleguides/python.md` §17.7 (lines 352-354).
|
||||
This is the baseline encoding — the imperative-ban style currently in production.
|
||||
Future variants will test alternative encodings (rationale-first, before/after, tabular) against this baseline.
|
||||
|
||||
**Source:** `conductor/code_styleguides/python.md:352-354`
|
||||
|
||||
---
|
||||
|
||||
### 17.7 The one exception: the boundary layer
|
||||
|
||||
The ONLY place these patterns are allowed is at the literal wire boundary — the function that calls `tomllib.load()`, `json.loads()`, or a vendor SDK's response parser. The boundary is 2-3 functions per file. Every consumer IMMEDIATELY converts to a typed dataclass via `from_dict()`.
|
||||
Reference in New Issue
Block a user