agents
This commit is contained in:
278
.opencode/skills/semantics-python/SKILL.md
Normal file
278
.opencode/skills/semantics-python/SKILL.md
Normal file
@@ -0,0 +1,278 @@
|
||||
---
|
||||
name: semantics-python
|
||||
description: Python-specific GRACE-Poly protocol: few-shot complexity examples, belief runtime patterns, module conventions, and FastAPI/SQLAlchemy patterns for ss-tools.
|
||||
---
|
||||
|
||||
#region Std.Semantics.Python [C:4] [TYPE Skill] [SEMANTICS python,examples,belief,fastapi,sqlalchemy]
|
||||
@BRIEF Python-specific HOW: few-shot complexity examples, belief runtime patterns, module decomposition, and FastAPI/SQLAlchemy conventions for the GRACE-Poly protocol in ss-tools.
|
||||
@RELATION DEPENDS_ON -> [Std.Semantics.Core]
|
||||
@RELATION DEPENDS_ON -> [Std.Semantics.Belief]
|
||||
@RELATION DEPENDS_ON -> [Std.Semantics.Contracts]
|
||||
|
||||
## 0. WHEN TO USE THIS SKILL
|
||||
|
||||
Load this skill when implementing Python backend code under the GRACE-Poly protocol in ss-tools. It provides concrete Python examples for each complexity tier, belief runtime patterns, FastAPI/SQLAlchemy conventions, and module structure rules. For generic protocol rules, see `semantics-core`. For contract enforcement methodology, see `semantics-contracts`.
|
||||
|
||||
## I. PYTHON BELIEF RUNTIME PATTERNS
|
||||
|
||||
ss-tools uses structured JSON logging for belief markers. The canonical pattern:
|
||||
|
||||
```python
|
||||
import logging
|
||||
import json
|
||||
from contextlib import contextmanager
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@contextmanager
|
||||
def belief_scope(contract_id: str):
|
||||
"""Thread-local belief frame for C4/C5 functions."""
|
||||
logger.info(json.dumps({"marker": "REASON", "contract": contract_id, "event": "enter"}))
|
||||
try:
|
||||
yield
|
||||
except Exception as e:
|
||||
logger.error(json.dumps({"marker": "EXPLORE", "contract": contract_id, "error": str(e)}))
|
||||
raise
|
||||
else:
|
||||
logger.info(json.dumps({"marker": "REFLECT", "contract": contract_id, "event": "exit"}))
|
||||
|
||||
# Usage:
|
||||
def reason(message: str, extra: dict = None):
|
||||
logger.info(json.dumps({"marker": "REASON", "message": message, **(extra or {})}))
|
||||
|
||||
def explore(message: str, extra: dict = None):
|
||||
logger.warning(json.dumps({"marker": "EXPLORE", "message": message, **(extra or {})}))
|
||||
|
||||
def reflect(message: str, extra: dict = None):
|
||||
logger.info(json.dumps({"marker": "REFLECT", "message": message, **(extra or {})}))
|
||||
```
|
||||
|
||||
**CRITICAL:** Do NOT manually type `[REASON]` in message strings. ALWAYS pass structured data through the `extra` dict. The `marker` field is the canonical protocol marker.
|
||||
|
||||
## II. PYTHON COMPLEXITY EXAMPLES
|
||||
|
||||
### C1 (Atomic) — DTOs, Pydantic schemas, simple constants
|
||||
```python
|
||||
# #region UserResponseSchema [C:1] [TYPE Class]
|
||||
from pydantic import BaseModel
|
||||
|
||||
class UserResponseSchema(BaseModel):
|
||||
id: str
|
||||
username: str
|
||||
email: str
|
||||
# #endregion UserResponseSchema
|
||||
```
|
||||
|
||||
### C2 (Simple) — Pure functions, utility helpers
|
||||
```python
|
||||
# #region format_timestamp [C:2] [TYPE Function] [SEMANTICS time,formatting]
|
||||
# @BRIEF Format a UTC datetime into a human-readable ISO-8601 string.
|
||||
from datetime import datetime
|
||||
|
||||
def format_timestamp(ts: datetime) -> str:
|
||||
return ts.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
# #endregion format_timestamp
|
||||
```
|
||||
|
||||
### C3 (Flow) — Module with nested functions, service layer
|
||||
```python
|
||||
# #region dashboard_migration [C:3] [TYPE Module] [SEMANTICS migration,dashboard]
|
||||
# @BRIEF Dashboard migration service — export/import dashboards with validation.
|
||||
# @LAYER Service
|
||||
|
||||
# #region migrate_dashboard [C:3] [TYPE Function] [SEMANTICS migration,dashboard]
|
||||
# @BRIEF Migrate a single dashboard from source to target Superset instance.
|
||||
# @RELATION DEPENDS_ON -> [SupersetClient]
|
||||
# @RELATION DEPENDS_ON -> [DashboardValidator]
|
||||
def migrate_dashboard(source_client, target_client, dashboard_id: str, db_mapping: dict) -> dict:
|
||||
dashboard = source_client.get_dashboard(dashboard_id)
|
||||
validate_dashboard(dashboard)
|
||||
mapped = apply_db_mapping(dashboard, db_mapping)
|
||||
result = target_client.import_dashboard(mapped)
|
||||
return result
|
||||
# #endregion migrate_dashboard
|
||||
|
||||
# #endregion dashboard_migration
|
||||
```
|
||||
|
||||
### C4 (Orchestration) — Stateful operations with belief runtime
|
||||
```python
|
||||
# #region run_migration_task [C:4] [TYPE Function] [SEMANTICS migration,task,state]
|
||||
# @BRIEF Execute a full migration task with rollback capability and progress reporting.
|
||||
# @PRE Database connection is established. Task record exists with valid migration plan.
|
||||
# @POST Task status updated to COMPLETED or FAILED. Migration audit log written.
|
||||
# @SIDE_EFFECT Modifies target Superset instance; writes task progress to DB; sends WebSocket updates.
|
||||
# @RELATION DEPENDS_ON -> [TaskManager]
|
||||
# @RELATION DEPENDS_ON -> [MigrationService]
|
||||
# @RELATION DEPENDS_ON -> [WebSocketNotifier]
|
||||
async def run_migration_task(task_id: str, db_session) -> dict:
|
||||
reason("Starting migration task", {"task_id": task_id})
|
||||
task = await db_session.get(Task, task_id)
|
||||
if not task:
|
||||
explore("Task not found", {"task_id": task_id})
|
||||
raise TaskNotFoundError(task_id)
|
||||
try:
|
||||
task.status = "RUNNING"
|
||||
await db_session.commit()
|
||||
reason("Task status set to RUNNING", {"task_id": task_id})
|
||||
result = await execute_migration_plan(task.migration_plan)
|
||||
task.status = "COMPLETED"
|
||||
task.result = result
|
||||
await db_session.commit()
|
||||
await notify_frontend(task_id, "completed", result)
|
||||
reflect("Migration completed successfully", {"task_id": task_id, "dashboards": len(result)})
|
||||
return result
|
||||
except Exception as e:
|
||||
explore("Migration failed, rolling back", {"task_id": task_id, "error": str(e)})
|
||||
task.status = "FAILED"
|
||||
task.error = str(e)
|
||||
await db_session.commit()
|
||||
await notify_frontend(task_id, "failed", {"error": str(e)})
|
||||
raise
|
||||
# #endregion run_migration_task
|
||||
```
|
||||
|
||||
### C5 (Critical) — With decision memory
|
||||
```python
|
||||
# #region rebuild_index [C:5] [TYPE Function] [SEMANTICS indexing,recovery,semantic]
|
||||
# @BRIEF Rebuild the full semantic index from source with atomic swap and rollback.
|
||||
# @PRE Workspace root is accessible. Source files exist.
|
||||
# @POST New index atomically swapped; old preserved for rollback.
|
||||
# @SIDE_EFFECT Reads all source files; writes index snapshot and checkpoint metadata.
|
||||
# @DATA_CONTRACT Input: WorkspaceRoot -> Output: IndexSnapshot + CheckpointManifest
|
||||
# @INVARIANT Index consistency: every contract_id in edges maps to an existing node.
|
||||
# @RELATION DEPENDS_ON -> [FileScanner]
|
||||
# @RELATION DEPENDS_ON -> [ContractParser]
|
||||
# @RELATION DEPENDS_ON -> [CheckpointWriter]
|
||||
# @RATIONALE Full rebuild needed because incremental update cannot detect deleted contracts.
|
||||
# @REJECTED Incremental-only update was rejected — it leaves stale edges when contracts
|
||||
# are deleted; only full scan guarantees consistency.
|
||||
def rebuild_index(root_path: str) -> dict:
|
||||
reason("Scanning source files", {"root": root_path})
|
||||
contracts = []
|
||||
for filepath in scan_files(root_path):
|
||||
try:
|
||||
parsed = parse_contract(filepath)
|
||||
contracts.append(parsed)
|
||||
except Exception as e:
|
||||
explore("Parse failure, skipping file", {"file": filepath, "error": str(e)})
|
||||
snapshot = {"contracts": contracts, "timestamp": datetime.utcnow().isoformat()}
|
||||
write_checkpoint(root_path, snapshot)
|
||||
reflect("Rebuild complete", {"contracts": len(contracts)})
|
||||
return snapshot
|
||||
# #endregion rebuild_index
|
||||
```
|
||||
|
||||
## III. PYTHON MODULE PATTERNS
|
||||
|
||||
### Project module layout (ss-tools convention)
|
||||
```
|
||||
backend/
|
||||
├── src/
|
||||
│ ├── api/ # FastAPI route handlers (C3)
|
||||
│ ├── core/ # Business logic core (C4/C5)
|
||||
│ │ ├── task_manager/ # Async task orchestration
|
||||
│ │ ├── auth/ # Authentication/authorization
|
||||
│ │ ├── migration/ # Dashboard migration logic
|
||||
│ │ └── plugins/ # Plugin system
|
||||
│ ├── models/ # SQLAlchemy models (C1/C2)
|
||||
│ ├── services/ # Business-logic services (C3/C4)
|
||||
│ └── schemas/ # Pydantic request/response schemas (C1)
|
||||
└── tests/ # pytest test modules
|
||||
```
|
||||
|
||||
### Module decomposition rules
|
||||
- Module files MUST stay < 400 LOC
|
||||
- Individual contract nodes MUST stay < 150 LOC, Cyclomatic Complexity ≤ 10
|
||||
- When limits are breached: extract into new modules with `@RELATION` edges
|
||||
- Use `__init__.py` for public re-exports only, not for logic
|
||||
- FastAPI route modules: one file per resource group (e.g., `dashboards.py`, `datasets.py`)
|
||||
|
||||
### Comment style
|
||||
- Python: `# #region ...` / `# #endregion ...`
|
||||
- Docstrings for metadata: `@TAG:` on separate lines within the region
|
||||
- Legacy `[DEF:...]` / `[/DEF:...]` recognized but new code uses region format
|
||||
|
||||
### FastAPI route pattern
|
||||
```python
|
||||
# #region dashboard_routes [C:3] [TYPE Module] [SEMANTICS api,dashboard]
|
||||
# @BRIEF Dashboard CRUD and migration API routes.
|
||||
# @RELATION DEPENDS_ON -> [DashboardService]
|
||||
# @RELATION DEPENDS_ON -> [AuthMiddleware]
|
||||
from fastapi import APIRouter, Depends
|
||||
|
||||
router = APIRouter(prefix="/api/dashboards", tags=["dashboards"])
|
||||
|
||||
# #region list_dashboards [C:2] [TYPE Function] [SEMANTICS api,query]
|
||||
# @BRIEF List dashboards with optional filters.
|
||||
@router.get("/")
|
||||
async def list_dashboards(
|
||||
page: int = 1,
|
||||
page_size: int = 20,
|
||||
service=Depends(get_dashboard_service)
|
||||
):
|
||||
return await service.list_dashboards(page, page_size)
|
||||
# #endregion list_dashboards
|
||||
|
||||
# #endregion dashboard_routes
|
||||
```
|
||||
|
||||
### SQLAlchemy model pattern
|
||||
```python
|
||||
# #region Dashboard [C:1] [TYPE Class]
|
||||
from sqlalchemy import Column, String, DateTime, JSON
|
||||
from sqlalchemy.orm import declarative_base
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
class Dashboard(Base):
|
||||
__tablename__ = "dashboards"
|
||||
id = Column(String, primary_key=True)
|
||||
title = Column(String, nullable=False)
|
||||
metadata = Column(JSON)
|
||||
created_at = Column(DateTime, server_default="now()")
|
||||
# #endregion Dashboard
|
||||
```
|
||||
|
||||
## IV. PYTHON VERIFICATION
|
||||
|
||||
```bash
|
||||
# Backend tests (from backend/ directory)
|
||||
cd backend && source .venv/bin/activate && python -m pytest -v
|
||||
|
||||
# With coverage
|
||||
python -m pytest --cov=src --cov-report=term-missing
|
||||
|
||||
# Ruff linting
|
||||
python -m ruff check src/ tests/
|
||||
|
||||
# Type checking (if mypy is configured)
|
||||
python -m mypy src/
|
||||
```
|
||||
|
||||
## V. FASTAPI / ASYNC PATTERNS
|
||||
|
||||
### Async belief scope
|
||||
```python
|
||||
import asyncio
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
@asynccontextmanager
|
||||
async def async_belief_scope(contract_id: str):
|
||||
"""Async belief frame for C4/C5 async functions."""
|
||||
reason("enter", {"contract": contract_id})
|
||||
try:
|
||||
yield
|
||||
except Exception as e:
|
||||
explore("error", {"contract": contract_id, "error": str(e)})
|
||||
raise
|
||||
else:
|
||||
reflect("exit", {"contract": contract_id})
|
||||
```
|
||||
|
||||
### Dependency injection convention
|
||||
- Use FastAPI `Depends()` for injecting services
|
||||
- Services are singletons or request-scoped
|
||||
- Never use global mutable state in service layer
|
||||
|
||||
#endregion Std.Semantics.Python
|
||||
Reference in New Issue
Block a user