package staging import ( "errors" "slices" ) // State enumerates the 7 lifecycle states a Record occupies. String // values are stable -- SQL implementations store them as TEXT and // downstream tooling (dashboards, CLI) matches on them verbatim. // // State 枚举 Record 生命周期的 7 个状态. 字符串值稳定 -- SQL 实现 // 以 TEXT 落表, 下游工具 (dashboard / CLI) 按字面匹配. type State string // State values. Four of them are terminal (see IsFinal); the other // three are in-flight. // // State 取值. 其中 4 个为终态 (见 IsFinal), 其余 3 个在途. const ( StatePendingTech State = "pending_tech" StateRejectedTech State = "rejected_tech" StatePendingML State = "pending_ml" StateRejectedML State = "rejected_ml" StateApproved State = "approved" StateExecuted State = "executed" StateFailed State = "failed" ) // IsFinal reports whether s is a terminal state (no legal outbound // transition). rejected_tech / rejected_ml / executed / failed are // terminal; the other three are in-flight. // // IsFinal 判定 s 是否终态 (无合法出向 transition). rejected_tech / // rejected_ml / executed / failed 为终态; 其余 3 个在途. func (s State) IsFinal() bool { switch s { case StateRejectedTech, StateRejectedML, StateExecuted, StateFailed: return true } return false } // transitions is the full legal transition matrix. Key is the source // state; value is the list of legal destinations. Any transition not // listed here is rejected by IsLegal with no additional side effect. // See doc.go for the triggering party per arc. // // transitions 是完整合法 transition 矩阵. key 为源状态, value 为 // 合法目的列表. 未列出的 transition 被 IsLegal 直接拒 (无其他副作用). // 触发方分工见 doc.go. var transitions = map[State][]State{ StatePendingTech: {StateRejectedTech, StatePendingML}, StatePendingML: {StateRejectedML, StateApproved}, StateApproved: {StateExecuted, StateFailed}, } // IsLegal reports whether from -> to is a legal transition per the // static matrix. Callers MUST call this before persisting any state // change; illegal transitions indicate a programming error or a // corrupted Record. // // IsLegal 不咨询 DependencyGuard -- guard 是在 IsLegal 返回 true 之后 // 的正交第二道闸. // // IsLegal 判定 from -> to 是否合法 (仅静态矩阵). 调用方持久化任何 // 状态变更前必须先调此函数; 非法 transition 代表编程错误或 Record // 已损坏. // // IsLegal 不咨询 DependencyGuard -- guard 是 IsLegal 返回 true 之后的 // 正交第二道闸. func IsLegal(from, to State) bool { dests, ok := transitions[from] if !ok { return false } return slices.Contains(dests, to) } // Sentinel errors for state-machine and store failures. Store and // Engine implementations wrap these with %w so callers classify with // errors.Is. // // 状态机与存储层的哨兵错误. Store 与 Engine 实现以 %w 包装, // 调用方用 errors.Is 分类. var ( // ErrIllegalTransition indicates a caller attempted to transition // a Record from a source state to an unreachable destination // (per the static transitions matrix) or to a state outside the // declared 7-state enum. // // ErrIllegalTransition 表示调用方试图将 Record 从源状态转到静态 // transitions 矩阵不可达的目的, 或转到 7 状态枚举之外的值. ErrIllegalTransition = errors.New("staging: illegal state transition") // ErrRecordNotFound is returned by Get / Update / Mark operations // when the id does not exist in the Store. // // ErrRecordNotFound 在 Get / Update / Mark 操作的 id 不存在时返回. ErrRecordNotFound = errors.New("staging: record not found") // ErrAlreadyFinal indicates an attempt to transition out of a // terminal state (rejected_tech / rejected_ml / executed / // failed). Distinct from ErrIllegalTransition so callers can // treat "already done" differently from "wire error". // // ErrAlreadyFinal 表示试图从终态 (rejected_tech / rejected_ml / // executed / failed) 转出. 与 ErrIllegalTransition 区分, 让调用方 // 可以把 "已完成" 和 "接线错" 分开处理. ErrAlreadyFinal = errors.New("staging: record already in final state") // ErrDependencyDenied is returned when DependencyGuard refuses // a transition that would otherwise be legal by the static // matrix. Engine callers retry (dependency may clear) or // surface to a human. // // ErrDependencyDenied 在 DependencyGuard 拒绝一个静态矩阵本来 // 放行的 transition 时返回. Engine 调用方可重试 (依赖可能清除) // 或上报人工. ErrDependencyDenied = errors.New("staging: dependency guard denied transition") )