# 领域模型:7 个核心对象 → 14 张表 方案 §5.3 列了 7 个核心领域对象。当前 SQLAlchemy 元数据包含 14 张表。 额外表用于事件溯源、恢复动作、资源锁、失败实例与审计;它们不是新的顶层产品概念, 而是核心对象的行为记录和读模型。 ## 对象 → 表 | 方案对象 | 表 | 补充说明 | |---|---|---| | DUT | `duts` | 序列号是防错盘的第一信号 | | Test Host | `test_hosts` + `oob_controllers` | 带外控制器独立成表:它必须能在主机死后独活 | | Firmware Artifact | `firmware_artifacts` | 哈希 + 签名 + 适用型号,缺一不可审计 | | Workflow | `workflows` | 存 spec 原文 + spec_hash,改一个字就是新版本 | | Run | `runs` + `run_steps` + `run_events` + `recovery_actions` | 见下 | | Failure Signature | `failure_signatures` + `run_failures` | 签名是聚类锚点,失败实例挂在它下面 | | Evidence Bundle | `evidence_bundles` | manifest 记录完整率,缺字段要能查出来 | | —(新增) | `resource_locks` | DUT/主机独占,防并发踩踏 | | —(新增) | `audit_log` | 谁在什么时候用哪个模板做了什么 | ## Run 为什么拆成四张表 `runs` 只存**当前投影**(状态、进度、结论)。真相在 `run_events`。 ``` run_events (append-only, 唯一真相) │ projection ▼ runs / run_steps / recovery_actions (可重建的读模型) ``` 任何时刻都能靠重放 `run_events` 重建 `runs` 的状态——这是"事件溯源"落地的含义, 也是统一时间线、审计、自动复现三个功能的共同地基。 **代价**:写路径必须走 `events/recorder.py`。仓储层故意**没有** `update_run_state()` 这种 API。想改状态?记一条事件,投影自己会跟上。 ## 关键字段的设计理由 ### `duts.allow_destructive` + `duts.serial` 破坏性动作(Format / Sanitize / 刷写)的三重校验:序列号匹配、 `allow_destructive=True`、设备路径不是系统盘。三个信号缺一个就拒绝执行。 误盘一次 = PoC 判定失败(方案 §9.3),所以这个字段不给 API 直接改, 只能走带审批记录的资产管理接口。 ### `runs.env_fingerprint`(JSON,不可变快照) 任务开始时冻结:主板 / BIOS / OS / 内核 / 驱动 / Agent 版本 / 工具版本 / DUT 固件。 A/B 对比的前提是环境相同——固件之外任何一项变了,对比结论就不成立。 签名哈希也吃这个指纹,所以"换了台机器复现不出来"能被自动识别为不同签名。 ### `runs.unattended_completion` + `runs.human_touches` 这两个字段是**销售武器**,不是技术指标。UCR(无人值守完成率)和人工触碰次数 是客户签字确认 ROI 的凭据(方案 §9.3、报告 Phase 2 现场指标)。 从第一行代码就记,不要等到要卖了才补。 ### `run_events.seq`(每个 run 内单调递增) 时间戳会因为主机断电、时钟漂移、带外控制器与主机时钟不同步而乱序。 `seq` 由控制平面单点分配,保证时间线可重放。带外事件与 Agent 事件 进同一条序列——双通道观测的交叉校验靠它。 ### `failure_signatures.hash`(8 要素) ``` hash(workflow_step, normalized_error_codes, log_templates, host_state, dut_enumeration_state, data_integrity_state, recovery_outcome, environment_fingerprint) ``` 只用错误码会把"掉盘"和"脚本超时"归成一类,聚类就废了。 `log_templates` 是日志模板化后的结果(数字/路径/时间被替换为占位符), 不是原始日志——否则每条日志都是新签名。 ### `run_failures.failure_class` `DUT_DEFECT` / `INFRA_FAILURE` / `SCRIPT_FAILURE` / `DATA_INTEGRITY` / `UNKNOWN`。 **`INFRA_FAILURE` 必须与 `DUT_DEFECT` 分开统计**(方案 §4.3)。 把网络断了、磁盘满了算成 SSD 缺陷,客户第一周就不信任这个系统了。 基础设施误报率 <5% 是 MVP 硬指标。 ## 状态字段一览 | 表 | 字段 | 取值 | |---|---|---| | `runs` | `state` | QUEUED / PREFLIGHT / RUNNING / RECOVERING / PAUSED / FROZEN / COMPLETED / ABORTED / REJECTED | | `runs` | `verdict` | PASS / FAIL / INCONCLUSIVE / null | | `run_steps` | `state` | PENDING / DISPATCHED / RUNNING / SUCCEEDED / FAILED / TIMED_OUT / SKIPPED / CANCELLED | | `test_hosts` | `status` | ONLINE / DEGRADED / OFFLINE / UNKNOWN | | `duts` | `status` | IDLE / IN_USE / QUARANTINED / MISSING | | `recovery_actions` | `outcome` | RECOVERED / FAILED / ESCALATED / FROZEN | 详见 [state-machine.md](state-machine.md)。