"""规则端点(C3 扩 / PRODUCT_SPEC §7 `POST /projects/:id/rules`;不变量 #3)。 POST /projects/:id/rules:作者显式加规则——审稿发现的问题/亮点随手沉淀为项目规则。 写一行 `rules`(level + content),喂给 assemble 的四级合并(`merge_rules`)。 加规则是**作者显式动作**(不变量 #3:规则入库不经 AI 静默写库)。`level` 合法性由 `RuleCreateRequest` 的 Literal 校验(非法 → FastAPI 422)。 提交边界:`RuleWriteRepo.create` 只 `flush()`,端点写后 `await session.commit()` (仿 foreshadow/outline 写侧,见 memory/gotchas)。 """ from __future__ import annotations import uuid from typing import Annotated from fastapi import APIRouter, Depends, Request from sqlalchemy.ext.asyncio import AsyncSession from ww_core.domain import RuleWriteRepo from ww_db import get_session from ww_api.logging_config import get_logger from ww_api.schemas.rules import RuleCreateRequest, RuleView from ww_api.services.project_deps import get_rule_write_repo log = get_logger("ww.api.rules") router = APIRouter(prefix="/projects", tags=["rules"]) RuleWriteRepoDep = Annotated[RuleWriteRepo, Depends(get_rule_write_repo)] SessionDep = Annotated[AsyncSession, Depends(get_session)] @router.post("/{project_id}/rules", status_code=201) async def create_rule( project_id: uuid.UUID, body: RuleCreateRequest, request: Request, repo: RuleWriteRepoDep, session: SessionDep, ) -> RuleView: """新增一条规则(201)。非法 level / 空 content → FastAPI 422。""" request_id = getattr(request.state, "request_id", None) view = await repo.create(project_id, level=body.level, content=body.content) await session.commit() log.info( "rule_created", project_id=str(project_id), request_id=request_id, level=body.level, ) return RuleView(level=view.level, content=view.content)