chore(repo): initialize team collaboration repository
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
# 架构:五层 → 代码目录的逐层映射
|
||||
|
||||
> **实现状态(2026-07-27)**:本文描述目标架构。当前已落地控制平面的模型、
|
||||
> 安全门禁、事件记录、Run 转移表、恢复决策、最小证据包、内置模拟执行器,以及
|
||||
> 独立 Host Agent 的注册、token、心跳和 Host 状态老化链路;Step 租约、Adapter
|
||||
> 执行、消息总线替换和生产对象存储仍未落地。
|
||||
|
||||
方案 §5.1 定义了五层架构。这里给出每一层**落到哪个目录、边界在哪、谁不能调用谁**。
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 交互与集成层 apps/console (Next.js) · CLI · Open API │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 控制平面 services/control-plane/flashops_control/ │
|
||||
│ services/ 编排 safety/ 门禁 api/ 接口 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 执行平面 services/host-agent/flashops_agent/ │
|
||||
│ adapters/ 工具适配器 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 带外平面 services/host-agent/flashops_agent/oob/ │
|
||||
│ (独立进程/独立网络,模拟实现在 simulator/) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 证据与智能层 control-plane/.../evidence/ · signatures/ │
|
||||
│ events/ 事件总线 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 依赖方向(单向,不可逆)
|
||||
|
||||
```
|
||||
console ──HTTP/SSE──▶ control-plane ◀──HTTP──── host-agent
|
||||
│ │
|
||||
│ ├─▶ adapters ─▶ 客户工具
|
||||
▼ │
|
||||
数据库 └─▶ oob ─▶ 带外控制器
|
||||
```
|
||||
|
||||
**硬规则:**
|
||||
|
||||
- `host-agent` **不得**导入 `control-plane` 的任何模块。两者只通过 `docs/agent-protocol.md`
|
||||
定义的 HTTP 契约通信。理由:Agent 要能单文件分发到客户 Windows 机器,
|
||||
控制平面代码不进客户测试主机。
|
||||
- `control-plane` **不得**主动连接 Agent(Agent 是拉模式)。理由:客户测试主机在
|
||||
内网/NAT 后面,反向连接在真实实验室里活不下来。
|
||||
- `engine/` **不得**直接读写数据库。它只操作 `Snapshot` 值对象,返回 `Effect` 列表。
|
||||
持久化由 `runtime.py` 施加。理由:状态机要能不起数据库单测。
|
||||
- `api/` **不得**包含业务判断。路由只做校验 + 调用 `engine`/`services`。
|
||||
- 任何危险动作(刷写、Format、Sanitize、断电)**必须**先过 `safety/`,
|
||||
且 `safety/` 的拒绝发生在下发给 Agent 之前。
|
||||
|
||||
## 控制平面内部分层
|
||||
|
||||
```
|
||||
flashops_control/
|
||||
├─ config.py 配置(产品名、DSN、路径、超时策略)
|
||||
├─ db.py 引擎/会话/建表
|
||||
├─ models/ SQLAlchemy 表定义(贫血模型,只管持久化)
|
||||
├─ schemas.py Pydantic 出入参(当前最小 API 契约)
|
||||
├─ events/
|
||||
│ ├─ bus.py 进程内事件总线(生产换 NATS/Redis,接口不变)
|
||||
│ └─ recorder.py 事件溯源写入:唯一允许改 Run 状态的入口
|
||||
├─ engine/
|
||||
│ ├─ states.py Run/Step 状态枚举 + 转移表(纯数据)
|
||||
│ ├─ recovery.py 五级恢复阶梯策略(纯函数)
|
||||
│ └─ (目标)planner/runtime:独立 Agent 接入后补齐
|
||||
├─ safety/
|
||||
│ ├─ gates.py DUT 绑定 / 系统盘保护 / 破坏性白名单
|
||||
│ └─ templates.py 签名命令模板注册表 + 参数 schema 校验
|
||||
├─ signatures/ (目标)失败签名(8 要素哈希)与聚类
|
||||
├─ evidence/ 最小 Evidence Bundle(manifest + 事件流)
|
||||
├─ services/runs.py 当前内置模拟编排(后续拆成 Agent + runtime)
|
||||
├─ seed.py 可重复执行的模拟资产与工作流
|
||||
└─ api/ FastAPI 路由
|
||||
```
|
||||
|
||||
**`engine/` 的纯度是刻意的。** 当前 `states.py` / `recovery.py`
|
||||
不导入 SQLAlchemy、不导入 FastAPI、不做 I/O、不看时钟
|
||||
(时间从 `Snapshot.now` 传入)。这让状态机的全部分支都能用普通 pytest 覆盖,
|
||||
不需要起库、不需要 sleep。后面几波会不断往状态机里加分支(新故障类型、
|
||||
新恢复策略、多工位调度),这条边界是唯一能防止它烂掉的东西。
|
||||
|
||||
## 开发形态 vs 生产形态
|
||||
|
||||
| 组件 | 开发(`make dev`) | 生产(`docker-compose`) | 换的时候动哪 |
|
||||
|---|---|---|---|
|
||||
| 数据库 | SQLite (aiosqlite) | PostgreSQL 16 | 只改 `DATABASE_URL` |
|
||||
| 队列/总线 | 进程内 asyncio | Redis / NATS | `events/bus.py` 换实现 |
|
||||
| 对象存储 | `var/objects/` 本地目录 | MinIO | `storage.py` 换实现 |
|
||||
| Agent | 内置模拟执行器 + 独立 Agent 心跳 | 客户主机上的 systemd/Windows 服务 | 补齐 Step lease/runtime |
|
||||
| 带外 | 模拟控制器 | 树莓派 / JetKVM / 智能 PDU | `oob/` 换实现 |
|
||||
|
||||
三处"换实现"都在接口后面,且开发态实现本身就是测试替身——不存在"生产才发现跑不通"。
|
||||
|
||||
## 为什么不用 Temporal / Airflow
|
||||
|
||||
见 [ADR-0003](decisions/0003-own-state-machine-over-temporal.md)。一句话:
|
||||
恢复语义(带外断电、冻结现场、误盘防护)是本产品的差异化本身,
|
||||
不能交给通用工作流引擎的重试模型。等到多工位跨节点调度成为瓶颈时再评估 Temporal,
|
||||
`runtime.py` 是唯一需要替换的文件。
|
||||
Reference in New Issue
Block a user