feat(v2): T06-a 方案维度解析(去重、刻度冲突、呈现判定)
T06 是大卡,按纪律拆子卡。本批为第一块:维度契约与解析。 新增 contracts:ScaleLevel、DimensionSpec。刻度层级必须升序且取值唯一 (乱序会让展示语义倒置);同 ID 必须标签/量纲/刻度层级完全一致。 新增 services/review_plan.py: - resolve_dimensions():按 dimension_id 合并;**同 ID 不同定义直接报冲突并拒绝**, 不让程序或模型临时挑一个(BR-28 / E-20);相同定义只留一份并合并组件引用, 避免重复计数 - presentation():AC-44 的呈现判定——0 维 empty / 2 维 list / 3–8 同刻度 radar / 9 维 grouped / 异刻度 faceted;硬阻断**单列**且永不进雷达面积 - assert_nothing_dropped():三组并集必须等于全部维度,"不默默丢失关键项" 有守卫 且守卫自身有测试证明会报错 刻度改版(同 scale_id 不同 version)视为不同定义——语义可能已变,不得共用雷达。 未完成:T06-b 方案装配与依赖冻结(AC-28)、T06-c 维度在结果中的落位属后续子卡。 测试:新增 test_review_plan.py 18 项(去重 8 + 呈现 10)。全量 333 项 OK(315 → 333); verify.py 10/10 PASS。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -527,3 +527,45 @@ class KnowledgeCard(Contract):
|
||||
if self.effective_at and self.expires_at and self.expires_at <= self.effective_at:
|
||||
raise ValueError("有效期必须晚于生效时间")
|
||||
return self
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 评审方案维度(T06)
|
||||
class ScaleLevel(Contract):
|
||||
"""刻度的一级。刻度由版本化标准驱动,**不由模型任意打分**(14 §5)。"""
|
||||
|
||||
value: int
|
||||
label: str = Field(min_length=1, max_length=80)
|
||||
criterion: str = Field(min_length=1, max_length=DESC_MAX)
|
||||
|
||||
|
||||
class DimensionSpec(Contract):
|
||||
"""核查维度定义(docs/v2/03-detailed-design.md §9.2「方案维度解析」)。
|
||||
|
||||
维度是 Skills/Rules 的**元数据**,不新增"维度卡"(14 §5)。同 ID 必须标签、
|
||||
量纲与刻度一致;不一致时**拒绝合并**而不是让模型临时猜(BR-28 / E-20)。
|
||||
"""
|
||||
|
||||
id: Identifier
|
||||
label: str = Field(min_length=1, max_length=80)
|
||||
order: int = Field(ge=0)
|
||||
scale_id: str = Field(min_length=1, max_length=ID_MAX)
|
||||
scale_version: str = Field(min_length=1, max_length=70)
|
||||
levels: list[ScaleLevel] = Field(min_length=2, max_length=10)
|
||||
component_refs: list[Ref] = Field(default_factory=list, max_length=64)
|
||||
required: bool = False
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _check_scale(self) -> DimensionSpec:
|
||||
values = [level.value for level in self.levels]
|
||||
if len(set(values)) != len(values):
|
||||
raise ValueError("同一刻度的层级取值不得重复")
|
||||
if values != sorted(values):
|
||||
raise ValueError("刻度层级必须按取值升序声明,避免展示时语义倒置")
|
||||
ref_ids = [ref.id for ref in self.component_refs]
|
||||
if len(set(ref_ids)) != len(ref_ids):
|
||||
raise ValueError("同一维度引用的组件不得重复")
|
||||
return self
|
||||
|
||||
def scale_key(self) -> tuple[str, str]:
|
||||
"""跨维度可比性的唯一判据:刻度标识 + 版本。"""
|
||||
return (self.scale_id, self.scale_version)
|
||||
|
||||
Reference in New Issue
Block a user