"""PMFlow OS read-side projection schemas.""" from __future__ import annotations from typing import Any, Literal, Optional from pydantic import BaseModel, Field PMFlowStage = Literal["idea", "validation", "planning", "development", "testing", "release", "growth"] PMFlowObjectStatus = Literal["draft", "confirmed", "in_progress", "blocked", "done", "released"] PMFlowRiskLevel = Literal["low", "medium", "high"] PMFlowExecutionStatus = Literal["queued", "pushed", "coding", "pr_open", "testing", "synced", "failed"] PMFlowReleaseStatus = Literal["check_pending", "ready", "shipping", "live", "watch"] PMFlowExecutionRuntimeTarget = Literal["local_bridge", "codex", "github_agent", "manual"] PMFlowExecutionProgressSource = Literal["local_bridge", "codex", "github_agent", "manual"] PMFlowExecutionProgressEventType = Literal[ "branch_created", "commit", "pr_opened", "pr_synced", "test_passed", "test_failed", "checks_passed", "failed", "blocked", "merged", "accepted", "manual_progress", ] PMFlowReleaseGateStatus = Literal["ready", "blocked", "needs_confirmation", "empty"] PMFlowReleaseValidationDecision = Literal["enter_release_candidate", "keep_blocked"] ProjectLoopStage = Literal["command_idle", "ai_extracting", "extraction_ready", "review_open", "writeback_success"] PMFlowChangeGroup = Literal["new_object", "field_update", "risk", "decision"] PMFlowChangeStatus = Literal["pending_review", "accepted", "edited", "deferred", "committed", "skipped"] PMFlowReviewerRoutingStatus = Literal["empty", "ready", "warning"] PMFlowReviewerRoutingSourceType = Literal["manual", "ai_extraction", "iteration_recommendation", "unknown"] PMFlowReviewerRoutingPriority = Literal["normal", "elevated", "urgent"] PMFlowReviewerConfidence = Literal["low", "medium", "high"] PMFlowFallbackReviewerRole = Literal["OWNER", "REVIEWER", "MEMBER"] PMFlowReviewDecisionAction = Literal["request_review", "approve", "request_changes", "reject"] PMFlowApprovalState = Literal["not_requested", "requested", "approved", "changes_requested", "rejected"] PMFlowObjectType = Literal[ "Requirement", "Decision", "Task", "Risk", "ModelDecision", "TestRun", "Release", "MetricSignal", ] class PMFlowThreadMessageCreateRequest(BaseModel): content: str = Field(min_length=1, max_length=12000) source: str = "web" attachments: list[dict[str, Any]] = Field(default_factory=list) class PMFlowThreadMessageResponse(BaseModel): id: str threadId: str projectId: str role: str author: str content: str createdAt: str status: str = "sent" source: str = "web" metadata: dict[str, Any] = Field(default_factory=dict) class PMFlowProposedChangeItem(BaseModel): group: PMFlowChangeGroup object_type: PMFlowObjectType object_id: Optional[str] = None title: str = Field(min_length=1, max_length=255) summary: str = Field(min_length=1) diff: str = Field(default="") payload: dict[str, Any] = Field(default_factory=dict) confidence: float = Field(default=0.0, ge=0.0, le=1.0) risk_level: PMFlowRiskLevel = "medium" class PMFlowExtractionCreateRequest(BaseModel): thread_id: str message_id: str scope: str = "latest_message" summary: Optional[str] = None headline: Optional[str] = None routing_meta: dict[str, Any] = Field(default_factory=dict) llm_call_id: Optional[str] = None proposed_items: list[PMFlowProposedChangeItem] = Field(min_length=1) class PMFlowChangeItemEditRequest(BaseModel): summary: Optional[str] = None diff: Optional[str] = None payload_patch: dict[str, Any] = Field(default_factory=dict) reason: Optional[str] = None class PMFlowChangeItemDeferRequest(BaseModel): reason: Optional[str] = None class PMFlowChangeSetCommitRequest(BaseModel): item_ids: list[str] = Field(default_factory=list) mode: Literal["atomic"] = "atomic" note: Optional[str] = None class PMFlowExecutionHandoffRequest(BaseModel): task_id: str = Field(min_length=1) repo_binding_id: Optional[str] = None runtime_target: PMFlowExecutionRuntimeTarget = "local_bridge" note: Optional[str] = Field(default=None, max_length=4000) class PMFlowExecutionHandoffConsumeRequest(BaseModel): runtime_target: Optional[PMFlowExecutionRuntimeTarget] = None consumer_label: Optional[str] = Field(default=None, max_length=120) note: Optional[str] = Field(default=None, max_length=4000) class PMFlowExecutionProgressEventRequest(BaseModel): task_id: str = Field(min_length=1) event_type: PMFlowExecutionProgressEventType source: PMFlowExecutionProgressSource = "manual" repo_binding_id: Optional[str] = None branch_name: Optional[str] = Field(default=None, max_length=255) commit_sha: Optional[str] = Field(default=None, max_length=255) pr_number: Optional[int] = Field(default=None, ge=1) test_summary: Optional[str] = Field(default=None, max_length=4000) workflow_state: Optional[str] = Field(default=None, max_length=120) summary: Optional[str] = Field(default=None, max_length=4000) event_source_id: Optional[str] = Field(default=None, max_length=255) occurred_at: Optional[str] = None class PMFlowReleaseValidationRunCreateRequest(BaseModel): decision: PMFlowReleaseValidationDecision note: Optional[str] = Field(default=None, max_length=4000) class PMFlowReleaseFeedbackIngestRequest(BaseModel): source_type: str = Field(default="manual", min_length=1, max_length=32) title: Optional[str] = Field(default=None, max_length=255) content: str = Field(min_length=1, max_length=12000) charter_id: Optional[str] = None metrics: dict[str, Any] = Field(default_factory=dict) samples: list[dict[str, Any]] = Field(default_factory=list) class PMFlowIterationRecommendationChangeSetDraftRequest(BaseModel): note: Optional[str] = Field(default=None, max_length=4000) class PMFlowIterationRecommendationGroupChangeSetDraftRequest(BaseModel): recommendation_ids: list[str] = Field(default_factory=list) note: Optional[str] = Field(default=None, max_length=4000) class PMFlowReviewDecisionRequest(BaseModel): action: PMFlowReviewDecisionAction reviewer_id: Optional[int] = Field(default=None, ge=1) reason: Optional[str] = Field(default=None, max_length=4000) item_ids: list[str] = Field(default_factory=list) class PMFlowChangeSetDetailResponse(BaseModel): changeSet: dict[str, Any] items: list[dict[str, Any]] = Field(default_factory=list) evidence: list[dict[str, Any]] = Field(default_factory=list) approvals: list[dict[str, Any]] = Field(default_factory=list) class PMFlowThread(BaseModel): id: str title: str projectId: str objective: str extractionStatus: Literal["idle", "loading", "ready"] = "idle" messageIds: list[str] = Field(default_factory=list) class PMFlowProjectSummary(BaseModel): id: str name: str goal: str stage: PMFlowStage threadId: str class PMFlowRequirement(BaseModel): id: str title: str summary: str stage: PMFlowStage = "planning" status: PMFlowObjectStatus linkedTaskIds: list[str] = Field(default_factory=list) class PMFlowDecision(BaseModel): id: str title: str summary: str stage: PMFlowStage = "planning" status: PMFlowObjectStatus owner: str = "未分配" class PMFlowTask(BaseModel): id: str title: str summary: str stage: PMFlowStage = "development" status: PMFlowObjectStatus assignee: str = "未分配" acceptanceCriteria: list[str] = Field(default_factory=list) requirementId: Optional[str] = None class PMFlowRisk(BaseModel): id: str title: str summary: str stage: PMFlowStage = "planning" status: PMFlowObjectStatus level: PMFlowRiskLevel = "medium" mitigation: str = "待补充缓解策略" class PMFlowModelDecision(BaseModel): id: str title: str summary: str stage: PMFlowStage = "planning" status: PMFlowObjectStatus rationale: str = "" class PMFlowTestRun(BaseModel): id: str title: str summary: str stage: PMFlowStage = "testing" status: PMFlowObjectStatus result: Literal["passed", "failed"] linkedTaskId: Optional[str] = None class PMFlowRelease(BaseModel): id: str title: str summary: str status: PMFlowReleaseStatus stage: PMFlowStage = "release" gateItems: list[dict[str, Any]] = Field(default_factory=list) class PMFlowMetricSignal(BaseModel): id: str title: str summary: str stage: PMFlowStage = "growth" status: PMFlowObjectStatus = "confirmed" value: str direction: Literal["up", "down", "flat"] = "flat" class PMFlowFeedEvent(BaseModel): id: str kind: Literal["extraction", "review", "execution", "test", "release", "growth", "agent"] title: str summary: str timestamp: str class PMFlowExecutionContext(BaseModel): taskId: Optional[str] = None status: PMFlowExecutionStatus = "queued" prompt: str = "" commitLog: list[str] = Field(default_factory=list) prStatus: str = "pending" testSummary: str = "pending" writebackStatus: str = "pending" class PMFlowTeamInbox(BaseModel): product: list[str] = Field(default_factory=list) engineering: list[str] = Field(default_factory=list) qa: list[str] = Field(default_factory=list) growth: list[str] = Field(default_factory=list) class PMFlowReviewerSuggestion(BaseModel): userId: Optional[str] = None displayName: Optional[str] = None role: Optional[str] = None reason: str confidence: PMFlowReviewerConfidence = "low" class PMFlowReviewerRoutingItem(BaseModel): projectId: str changeSetId: str changeSetTitle: str changeSetStatus: str sourceType: PMFlowReviewerRoutingSourceType = "unknown" reviewReason: str riskLevel: PMFlowRiskLevel = "low" priority: PMFlowReviewerRoutingPriority = "normal" changeTypes: list[str] = Field(default_factory=list) itemCount: int = 0 pendingItemCount: int = 0 evidenceCount: int = 0 suggestedReviewer: Optional[PMFlowReviewerSuggestion] = None fallbackReviewerRole: Optional[PMFlowFallbackReviewerRole] = None reviewDecision: Optional[PMFlowReviewDecisionAction] = None approvalState: PMFlowApprovalState = "not_requested" requestedAt: Optional[str] = None decidedAt: Optional[str] = None decisionReason: Optional[str] = None targetUrl: str createdAt: Optional[str] = None updatedAt: Optional[str] = None class PMFlowReviewerRoutingSummary(BaseModel): totalPendingReview: int = 0 highRiskCount: int = 0 missingReviewerCount: int = 0 iterationDraftCount: int = 0 requestedReviewCount: int = 0 approvedCount: int = 0 changesRequestedCount: int = 0 rejectedCount: int = 0 class PMFlowReviewerRoutingProjection(BaseModel): status: PMFlowReviewerRoutingStatus = "empty" warningCode: Optional[str] = None items: list[PMFlowReviewerRoutingItem] = Field(default_factory=list) summary: PMFlowReviewerRoutingSummary = Field(default_factory=PMFlowReviewerRoutingSummary) class PMFlowProjectMemory(BaseModel): truthNodeFacts: list[dict[str, Any]] = Field(default_factory=list) semanticFacts: list[dict[str, Any]] = Field(default_factory=list) sourceSummary: dict[str, int] = Field(default_factory=dict) class ProjectTruthStateProjection(BaseModel): loopStage: ProjectLoopStage = "command_idle" lastWritebackAt: Optional[str] = None project: PMFlowProjectSummary thread: PMFlowThread messages: list[dict[str, Any]] = Field(default_factory=list) changeSets: list[dict[str, Any]] = Field(default_factory=list) changeItems: list[dict[str, Any]] = Field(default_factory=list) evidence: list[dict[str, Any]] = Field(default_factory=list) requirements: list[PMFlowRequirement] = Field(default_factory=list) decisions: list[PMFlowDecision] = Field(default_factory=list) tasks: list[PMFlowTask] = Field(default_factory=list) risks: list[PMFlowRisk] = Field(default_factory=list) modelDecisions: list[PMFlowModelDecision] = Field(default_factory=list) testRuns: list[PMFlowTestRun] = Field(default_factory=list) releases: list[PMFlowRelease] = Field(default_factory=list) metricSignals: list[PMFlowMetricSignal] = Field(default_factory=list) approvals: list[dict[str, Any]] = Field(default_factory=list) feed: list[PMFlowFeedEvent] = Field(default_factory=list) teamInbox: PMFlowTeamInbox = Field(default_factory=PMFlowTeamInbox) reviewerRouting: PMFlowReviewerRoutingProjection = Field(default_factory=PMFlowReviewerRoutingProjection) activeChangeSetId: Optional[str] = None selectedTaskId: Optional[str] = None execution: PMFlowExecutionContext = Field(default_factory=PMFlowExecutionContext) projectMemory: PMFlowProjectMemory = Field(default_factory=PMFlowProjectMemory) projectionWarnings: list[str] = Field(default_factory=list) class ExecutionSummaryProjection(BaseModel): projectId: str execution: PMFlowExecutionContext releaseGate: dict[str, Any] = Field(default_factory=dict) releaseSignals: dict[str, Any] = Field(default_factory=dict) devSync: dict[str, Any] = Field(default_factory=dict) promptPackage: Optional[dict[str, Any]] = None repoBindings: list[dict[str, Any]] = Field(default_factory=list) activeDevTasks: list[dict[str, Any]] = Field(default_factory=list) recentProgressEvents: list[dict[str, Any]] = Field(default_factory=list) recentDeliveries: list[dict[str, Any]] = Field(default_factory=list) warnings: list[str] = Field(default_factory=list)