# 执行进度记录 > 本文件只追加记录任务执行、验证、阻塞和关键决策。当前目录、当前命令和下一任务以 [docs/current-state.md](docs/current-state.md) 为准。 ## 记录格式 ```markdown ## 【YYYY-MM-DD】T-【编号】 【任务名】 - 状态:【DONE / BLOCKED / PARTIAL】 - 变更:【文件或模块】 - 验证:【真实命令和结果】 - 阻塞:【原因与决策人】 - 决策:【本轮确定的取舍】 - 下一步:【任务 ID 或待确认事项】 ``` ## 执行记录 ## 【2026-07-20】T-000 建立 Harness Coding 文档基线 - 状态:DONE - 变更:创建仓库级 agent 入口、V1/V2 需求、技术栈、架构、任务看板、模块合约、当前状态和实施计划文档。 - 验证:`./init.ps1` 已通过:Python 3.8.10 可导入 OpenCV、NumPy、Ultralytics 8.3.205 与 PyQt5,且 `python -m compileall -q demo` 退出码 0;`demo/best.pt` 被识别为 `pose`、`kpt_shape=[17, 3]`、类别 `person`;20 个 Markdown 文档的本地链接均可解析,实施计划没有占位标记。全局 `pip check` 的其他项目冲突不作为本项目验收;`init.ps1` 检查实际依赖并对非零退出码失败。 - 阻塞:无代码 blocker;V1 依赖清单、RTSP 实流录像和现场验收录像尚未创建。 - 决策:`demo/` 作为旧基线保留;V1 使用 Python + PyQt5 + OpenCV + Ultralytics;V2 使用 Go,且必须以 V1 的录像回归集和 ONNX 一致性测试为迁移门槛。 - 下一步:T-101。 ## 【2026-07-20】DESIGN UI/UX 客户演示原型规格 - 状态:PARTIAL - 变更:创建 `docs/ui/silver-pose-ui-ux-spec.md`,定义离线 HTML 原型的正常监控、确认摔倒、连接异常状态、布局、视觉令牌、键盘操作和无障碍验收条件。 - 验证:`./init.ps1` 通过;规格共 98 行,未发现 `TBD`、`TODO` 或 `REPLACE` 占位符,`git diff --check` 通过。 - 阻塞:等待用户审阅已提交的规格;未生成 HTML,未变更 `demo/`、`v1/` 或任务看板状态。 - 决策:原型为单页、无网络依赖的视觉与交互演示,使用虚构摄像头和事件数据,不表现为真实 RTSP 或模型推理。 - 下一步:用户确认规格后,创建 `docs/ui/silver-pose-v1-prototype.html` 并执行离线交互验收。 ## 【2026-07-20】DESIGN 离线 HTML UI/UX 原型 - 状态:DONE - 可视化验证:使用本机 Chrome 无界面模式实际渲染 1280 × 800、900 × 900、600 × 1000 三种窗口截图;宽屏与窄屏截图已人工检查,主操作可见,窄屏下右栏纵向重排,未见横向溢出。Chrome 对 file 页面返回非零退出码,但三张 PNG 均实际生成,故以文件与画面结果作为结论。 - 变更:创建 docs/ui/silver-pose-v1-prototype.html 和实施计划 docs/ui/2026-07-20-html-prototype-plan.md。原型可在单页中通过按钮或数字键 1、2、3 切换正常监控、确认摔倒和连接异常;包含 16:9 模拟姿态画面、红色确认状态、一次性演示弹窗、报警/截图证据、最近事件、窄窗口重排、键盘焦点和屏幕阅读器状态提示。 - 验证:Node.js 22 解析唯一内联脚本,并确认三种状态按钮、弹窗、确认按钮、aria-live 与 1007/640/600 epx 响应式断点存在,输出 prototype static validation passed;敏感与网络边界扫描未发现凭证、模型路径或网络行为标记;./init.ps1 通过。 - 阻塞:无。该文件是离线视觉原型,已做静态和无界面浏览器布局验证,但不表示实时 RTSP、模型推理、声音播放或截图保存已实现。 - 决策:原型采用单窗口深色控制台布局,以同一画面状态切换展示客户关心的“正常不报警、确认后完整报警闭环、断流不误报”三种结果。 - 下一步:T-101;HTML 原型可作为后续 T-201 PyQt 界面实现的视觉与交互参考。 ## 【2026-07-20】DESIGN 顶部 Tab 与设置原型修订 - 状态:DONE - 变更:更新需求、页面导航、架构、T-201 验收、UI 规格和实施计划;将 HTML 原型改为顶部双 Tab。实时监控 Tab 保留姿态与报警演示;设置 Tab 显示来源环境变量就绪、模型只读信息、六项非敏感事件或重连参数、草稿版本与保存/重置操作。 - 验证:基线 ./init.ps1 通过;Node.js 22 解析唯一内联脚本,并检查 tablist、tab、tabpanel、设置表单、保存操作、运行配置版本、配置草稿控制器和三档响应式断点;敏感与网络边界扫描未发现凭证、模型路径或网络行为标记。Chrome 无界面模式实际渲染 1280 宽监控页、1280/900/600 宽设置页;宽屏与 600 宽截图已人工检查,未见横向溢出。 - 阻塞:无。设置页只模拟草稿编辑和保存反馈,不会写入本地配置,也不会热改监控中的运行配置。 - 决策:V1 固定采用顶部双 Tab,而非左侧导航;两个静态目的地不会挤压实时视频。设置草稿在下一次开始监控时才成为新的运行配置,事件工件继续追溯实际运行版本。 - 下一步:T-101;后续 T-201 按此交互和配置快照边界实现 PyQt 界面。 ## 【2026-07-20】DESIGN 浅色 Windows 主题规范 - 状态:DONE - 变更:更新 UI 规格、页面规则、编码规则和原型实施计划。正式默认主题设为浅灰蓝页面背景与白色卡片;红色只表示确认摔倒事件、对应弹窗和事件证据。正常、疑似、连接异常、保存与校验改用绿色、琥珀色、灰蓝或标准主色,并保留文字和图标提示。 - 验证:./init.ps1 通过;已核对 UI 规格、页面规则和编码规则中的主题语义一致。 - 阻塞:按本轮用户范围未修改 HTML。现有 HTML 为深色历史原型,不能作为正式色彩实现来源,需在后续单独重绘。 - 决策:后续 PyQt T-201 与任何新的 HTML 原型必须以浅色 Windows 主题规范为准,不得将红色用于非确认摔倒场景。 - 下一步:等待用户授权,将 docs/ui/silver-pose-v1-prototype.html 重绘为浅色主题。 ## 【2026-07-20】DESIGN 浅色 Windows HTML 原型修订 - 状态:DONE - 变更:新增 `docs/ui/silver-pose-v2-prototype.html` 作为独立离线原型。它使用浅灰蓝页面背景、白色卡片和集中 CSS 令牌;红色只用于确认摔倒、告警弹窗和事件证据。保留顶部双 Tab、正常/确认/断流三种状态、一次性告警弹窗、设置草稿及下一次启动生效边界;未改动历史深色 v1 原型。 - 验证:Node.js 22 已解析唯一内联脚本,并检查状态控件、Tab、弹窗、三个响应式断点及浅色主题令牌;隐私与网络边界扫描未发现凭证、模型路径或网络行为;`./init.ps1` 通过。Chrome 无界面模式实际渲染了正常、确认摔倒、连接异常、900 宽设置和 600 宽设置画面;确认摔倒显示红色弹窗,连接异常不显示红色告警,窄宽度为纵向重排。 - 阻塞:无。原型不连接真实摄像头、模型、声音或文件系统;截图路径和事件均为演示数据。 - 决策:文件名中的 v2 仅表示 HTML 视觉修订,不启动 Go V2,也不改变 V1 验收和 ONNX 一致性门槛。 - 下一步:T-101;后续 T-201 以 v2 HTML 和 UI/UX 规格实现 PyQt 浅色 Windows 界面。 ## 【2026-07-21】T-101 创建 V1 包、依赖清单、示例配置和忽略规则 - 状态:DOING - 变更:任务已从 TODO 落为 DOING;尚未创建 V1 生产代码。 - 验证:`./init.ps1` 已通过。`python -m pytest v1/tests -v` 当前以 `v1/tests` 不存在退出,这是 T-101 建立 pytest 基线前的预期初始失败。 - 阻塞:无。 - 决策:`init.ps1` 保持无副作用的依赖检查与基线验证,不自动安装包;`requirements.txt` 将作为显式、可复现的安装清单。 - 下一步:先写配置加载与敏感值拒绝的失败测试,再实现最小配置接口。 ## 【2026-07-21】T-101 创建 V1 包、依赖清单、示例配置和忽略规则(完成) - 状态:DONE - 变更:新增 `v1` 包、经验证的 `config.py`、仅含环境变量名的公开配置示例、与当前环境一致的 `requirements.txt` 和两项 pytest 配置测试;新增 `.gitignore`,忽略本地配置、事件工件、私有录像和模型导出物。`init.ps1` 现在运行 V1 测试但不自动安装依赖。 - 验证:先运行 `python -m pytest v1/tests/test_config.py -v`,确认因缺少 `v1.config` 产生预期导入失败;实现后 `./init.ps1` 和 `python -m pytest v1/tests -v` 均通过(2 passed),`python -m compileall -q v1 demo` 通过。公开配置的地址/凭证扫描无匹配,`git check-ignore` 已确认本地配置、事件工件、`.pt` 与 `.onnx` 工件被忽略。 - 阻塞:无。 - 决策:配置只允许 `rtsp_url_env` 指向运行环境变量,拒绝内嵌来源地址;确认窗口限制为 1–3 秒。公开示例使用 64 位全零哈希占位,T-103 必须替换为受控模型真实哈希后才可加载模型。 - 下一步:T-102,先建立录像回放和错误来源的失败测试。 ## 【2026-07-21】T-102 实现可重连的视频源与录像回放适配器 - 状态:DOING - 变更:任务已从 TODO 落为 DOING;尚未创建视频源生产代码。 - 验证:继承 T-101 的 `./init.ps1` 基线通过记录;本任务将先以可控假捕获器建立单元测试,再以本地演示录像作 smoke 验收。 - 阻塞:无。 - 决策:视频源只返回帧、单调时间戳和来源状态;读取失败、EOF 和重连绝不生成虚构人员或摔倒事件。 - 下一步:写入时间戳单调、错误来源和有界重连的失败测试。 ## 【2026-07-21】T-102 实现可重连的视频源与录像回放适配器(完成) - 状态:DONE - 变更:新增 `v1/video_source.py`,定义 `FramePacket` 与 `connected`、`retrying`、`error`、`eof`、`closed` 来源状态。录像优先读取容器时间戳,负首帧或倒退值回退到帧序号/FPS;可重连来源采用有界指数退避,所有非连接状态返回空帧且不携带事件信息。 - 验证:先运行 `python -m pytest v1/tests/test_video_source.py -v`,确认因缺少 `v1.video_source` 导入失败;实现后视频源测试 4 passed,完整 V1 测试在本轮最后一次运行时为 6 passed。`demo/1.mp4` 只读 smoke 成功读取首两帧,时间戳为 0.000000 与 0.033333 秒;`python -m compileall -q v1 demo` 通过。 - 阻塞:无。 - 决策:来源失败只表达来源状态,绝不以空帧、EOF 或重连触发人员更新或摔倒事件;本地录像测试使用临时生成的 AVI,避免将客户视频或样例录像作为 pytest 夹具。 - 下一步:T-103,先写模型来源和 17 点 Pose 输出的失败测试。 ## 【2026-07-21】T-103 实现 Pose 适配器与模型来源校验 - 状态:DOING - 变更:任务已从 TODO 落为 DOING;尚未创建 Pose 生产代码。 - 验证:继承 T-102 的 `./init.ps1` 与 6 项 V1 测试通过记录;本任务将先用不依赖真实权重的假 YOLO 结果建立 17 点解析测试,再执行只读模型 smoke。 - 阻塞:无。 - 决策:构造适配器时必须校验模型 SHA-256、任务为 pose、类别为 person、关键点形状为 17×3;不把任何输出命名为摔倒概率。 - 下一步:写入模型来源错误与 person Pose 输出形状的失败测试。 ## 【2026-07-21】T-103 实现 Pose 适配器与模型来源校验(完成) - 状态:DONE - 变更:新增 `v1/pose.py`,以 SHA-256 锁定模型来源,要求任务为 pose、存在 person 类且关键点形状严格为 17×3;输出不含跟踪 ID 的 `PersonPose`(box、box 置信度、17 个三元关键点)。 - 验证:先运行 `python -m pytest v1/tests/test_pose.py -v`,确认因缺少 `v1.pose` 导入失败;实现后 Pose 测试 3 passed,完整 V1 测试在本轮最后一次运行时为 9 passed。对 `demo/best.pt` 的只读 smoke 使用实际 SHA-256 加载模型,并从 `demo/1.mp4` 首帧得到 2 名人员、每人 17 点;`python -m compileall -q v1 demo` 通过。 - 阻塞:无。 - 决策:模型输出绝不称为摔倒概率;人员 ID 由 T-104 在 Pose 输出之后分配,模型不匹配或结果形状损坏时必须显式报错而不是继续推理。 - 下一步:T-104,先写缺失下肢关键点拒绝和水平姿态只作为证据的失败测试。 ## 【2026-07-21】T-104 实现人员跟踪与姿态质量门控 - 状态:DOING - 变更:任务已从 TODO 落为 DOING;尚未创建跟踪或证据生产代码。 - 验证:继承 T-103 的 `./init.ps1`、9 项 V1 测试和真实模型 smoke 通过记录;本任务将先建立相邻帧 ID 稳定性、缺失下肢点拒绝和水平几何仅作为证据的失败测试。 - 阻塞:无。 - 决策:质量与证据模块只能返回质量/几何事实,不能返回报警或直接创建事件;空帧和低质量姿态不可推进倒地候选。 - 下一步:写入 T-104 的失败测试。 ## 【2026-07-21】T-104 实现人员跟踪与姿态质量门控(完成) - 状态:DONE - 变更:新增 `v1/tracking.py`,以归一化框中心距离为连续人员分配稳定 ID;新增 `v1/evidence.py`,要求肩、髋、膝、踝关键点全部超过阈值,再输出水平躯干角度和相对躯干长度归一化的下移证据。 - 验证:先运行 `python -m pytest v1/tests/test_evidence.py -v`,确认因缺少 `v1.evidence` 导入失败;实现后证据/跟踪测试 5 passed,完整 V1 测试在本轮最后一次运行时为 14 passed,`python -m compileall -q v1 demo` 通过。 - 阻塞:无。 - 决策:`PoseEvidence` 只表达质量和几何事实,不能报警或创建事件;空姿态、缺失下肢点或退化躯干都会停止证据积累。T-105 必须以 `track_id`、单调时间和这些证据来确认事件。 - 下一步:T-105,先写持续倒地只产生一次事件、短时弯腰不报警与恢复后的新事件边界测试。 ## 【2026-07-21】T-105 实现按 ID 的时序摔倒状态机 - 状态:DOING - 变更:任务已从 TODO 落为 DOING;尚未创建状态机生产代码。 - 验证:继承 T-104 的 `./init.ps1` 与 14 项 V1 测试通过记录;本任务将先写持续倒地确认一次、短时弯腰无事件、恢复后才允许新事件和拒绝证据不推进时间的失败测试。 - 阻塞:无。 - 决策:状态机只接收按 ID 的可用证据与单调秒数;视频帧率、空帧、来源断流和拒绝姿态都不能缩短确认窗口或创建事件。 - 下一步:写入 T-105 失败测试。 ## 【2026-07-21】T-105 实现按 ID 的时序摔倒状态机(完成) - 状态:DONE - 变更:新增 `v1/fall_state.py`,为每个 `track_id` 独立维护 NORMAL、SUSPECT、CONFIRMED、RECOVERING。只有连续、可用的倒地候选达到 1–3 秒确认窗口才产生一个 `FallEvent`;确认后持续帧不会重复返回事件,拒绝证据会打断疑似持续时间,恢复证据持续后才回到 NORMAL。 - 验证:先运行 `python -m pytest v1/tests/test_fall_state.py -v`,确认因缺少 `v1.fall_state` 导入失败;实现后状态机测试 6 passed,完整 V1 测试在本轮最后一次运行时为 20 passed,`python -m compileall -q v1 demo` 通过。 - 阻塞:无任务内 blocker。当前尚未创建把 Pose/跟踪/证据喂给状态机的应用编排层,因此不能把这些单元测试误称为真实视频摔倒检测验收。 - 决策:确认窗口在构造时强制限制为 1–3 秒;同一人员的时钟与事件序号隔离。声音、弹窗、截图与 JSONL 仍由 T-201/T-202 实现,状态机不执行这些副作用。 - 下一步:T-201,实现顶部双 Tab 的 PyQt 监控/设置界面,并保持其不直接执行事件判断。 ## 【2026-07-21】T-106 装配事件管线与固化事件契约 - 状态:DOING - 变更:根据 `docs/review/2026-07-21-t101-t105-review.md` 在 T-105 与 T-201 之间新增 T-106;T-201 依赖改为 T-106。任务将补齐 PoseEvidence 到状态机 Evidence 的领域映射、缺失/低质量证据中断、配置字段实际生效、配置版本可追溯,以及录像/实时流的显式来源行为。 - 验证:开始前 `./init.ps1` 退出码 0,V1 单元测试 20 passed。 - 阻塞:无。 - 决策:不把事件判断规则混入 T-201 的 PyQt UI;T-106 先提供真实、可测试的管线输出。`demo/` 保持未跟踪旧基线,不纳入本任务提交。 - 下一步:先为领域规则、缺失证据、配置版本和来源模式写失败测试,再作最小实现。 ## 【2026-07-21】T-106 装配事件管线与固化事件契约(完成) - 状态:DONE - 变更:新增 `v1/fall_policy.py`,将快速下移后在 suspect 窗口内形成的水平姿态映射为候选,并只在确认/恢复状态处理恢复证据;新增 `v1/pipeline.py`,将 Pose、跟踪、质量、几何证据、领域规则和按 ID 状态机连接。缺失人员、低质量姿态和非连接帧均向状态机输入拒绝证据,不能跨空档确认。`FallEvent` 现在包含运行配置的非敏感 `config_version`;`cooldown_seconds` 在确认后延迟恢复判断。视频源新增显式 `SourceMode.REPLAY`/`STREAM`:录像 EOF 不重放,实时流以收帧单调时钟计时并重连。 - 验证:以失败测试先后覆盖领域规则、配置版本、cooldown、Replay EOF、Stream 计时、完整管线确认及缺帧中断;最终 `python -m pytest v1/tests -v` 为 30 passed,`python -m compileall -q v1 demo` 退出码 0。只读真实 smoke 使用 `demo/best.pt` 与 `demo/1.mp4` 驱动 T-106 管线两帧,结果为首帧 `connected/2 people/0 events`、第二帧 `connected/0 people/0 events`。 - 阻塞:无任务内 blocker。该 smoke 未使用带标签摔倒/反例录像,不能作为事件准确率、1–3 秒延迟或海康 RTSP 现场验收证据。 - 决策:`suspect_window_seconds` 定义为“快速下移到水平姿态”的最大间隔,`cooldown_seconds` 定义为确认后开始恢复判断的最短等待时间;运行配置版本排除 RTSP 地址和凭证。不会仅为延长确认而增大 tracker 的身份寿命,证据连续性由管线的拒绝证据保证。当前轻量跟踪多人交叉换 ID 记录为已知限制。 - 下一步:T-201,实现顶部双 Tab 的 PyQt 监控与设置界面,只消费 T-106 的管线输出,不执行事件判定。 ## 【2026-07-21】T-201 实现 PyQt 顶部双 Tab 的监控与设置界面 - 状态:DOING - 变更:任务从 TODO 落为 DOING;尚未创建 GUI 生产代码。 - 验证:开始前 V1 单元测试 30 passed。本环境为 WSL/Linux 且未安装 PyQt5(`import PyQt5` 失败),GUI 渲染层的可视化验收必须在 Windows 上执行。 - 阻塞:无任务内 blocker;PyQt5 渲染冒烟受环境限制,见决策。 - 决策:把可验收的 UI 逻辑(设置草稿与运行配置快照隔离、状态→语义色映射、骨架与人员叠加数据推导、连接状态文案)抽到不依赖 Qt 的 `v1/view_model.py`,在本环境用 pytest 真实覆盖;`v1/gui.py` 与 `v1/app.py` 仅作薄 Qt 外壳与装配,只消费 T-106 管线输出,不执行事件判定。GUI 的可视化冒烟标注为需 Windows + PyQt5 执行。 - 下一步:先写 view-model 的失败测试(草稿隔离、状态色、骨架推导、连接文案),再实现最小 UI 外壳。 ## 【2026-07-21】T-201 实现 PyQt 顶部双 Tab 的监控与设置界面(完成) - 状态:DONE - 变更:新增 `v1/view_model.py`(Qt-free 可测核心):由 `FrameAnalysis` 构建监控视图状态(连接文案、状态→语义色、COCO-17 骨架段、人员 box/ID/状态标签,红色只用于 CONFIRMED,非连接帧不产生人员或摔倒标签),以及 `SettingsDraft`(运行快照/已保存/草稿三份隔离,编辑只改草稿、保存只标下次启动生效、`start_monitoring` 才提升为新运行配置并变更配置版本)。新增 `v1/gui.py` 薄 PyQt5 外壳(顶部双 Tab、`QTabWidget`、`VideoView` 用 `QPainter` 叠加、设置 `QDoubleSpinBox` 绑定草稿、浅色 Windows 主题 QSS)与 `v1/app.py` 装配(`FrameWorker(QThread)` 持有视频源与 `FallPipeline` 并只发出已判定的 `FrameAnalysis`,窗口只渲染;开始监控时按草稿生成不可变运行配置快照)。 - 验证:新增 `v1/tests/test_view_model.py` 9 项,覆盖状态色(红仅 CONFIRMED)、断流不产生摔倒标签、低置信关键点被剔除、事件徽标、草稿隔离/保存/下次启动提升、越界拒绝与配置版本敏感性;`python3 -m pytest v1/tests -q` 为 39 passed;`python3 -m compileall -q v1 demo` 退出码 0(含 `gui.py`、`app.py` 语法)。真实链路冒烟:以 `demo/1.mp4` 真实解码 + 真实 `FallPipeline/PersonTracker/FallEvidencePolicy/FallStateMachine` + 确定性假 Pose 适配器驱动 `build_monitor_view`,连续帧得到稳定 `P-0001`、box、14 段骨架、17/17 关键点、`NORMAL/success`;注入 RETRYING 帧得到“正在重连…”、0 人、offline 色。 - 阻塞:无任务内 blocker。当前 WSL/Linux 环境未安装 PyQt5,且本机 ultralytics/torch 版本与 Windows 基线(8.3.205)不兼容导致模型前向不可用;因此 `gui.py`/`app.py` 的可视化冒烟与真实模型端到端渲染必须在 Windows + PyQt5 上执行,不能以本轮 CI 结果冒充可视化验收。 - 决策:把全部可验收 UI 逻辑抽到 Qt-free 的 `view_model.py` 并在本环境真实测试,Qt 渲染层保持最薄且不含事件判定;设置草稿采用“运行快照/已保存/草稿”三份隔离,运行配置版本只在开始监控时变更,使事件与截图可追溯 `config_version`。 - 下一步:T-202,实现 CONFIRMED 的一次性声音、弹窗、带标注截图与 JSONL 事件日志。 ## 【2026-07-21】T-202 实现本地报警、弹窗、截图和 JSONL 日志 - 状态:DOING - 变更:任务从 TODO 落为 DOING;尚未创建报警工件生产代码。 - 验证:开始前 V1 单元测试 39 passed。 - 阻塞:无任务内 blocker;声音播放与 Qt 弹窗需 Windows 桌面,见决策。 - 决策:报警派发按 `event_id` 去重,只对首次 CONFIRMED 触发一次副作用;截图标注与 JSONL 写入在本环境用 pytest 真实覆盖;声音与弹窗抽象为可注入 sink,Windows 侧提供真实实现。JSONL 与截图文件名不含 RTSP 地址、凭证或客户姓名,只用事件 ID 与日期目录。 - 下一步:先写去重、JSONL 字段与截图落盘的失败测试,再实现最小报警工件模块。 ## 【2026-07-21】T-202 实现本地报警、弹窗、截图和 JSONL 日志(完成) - 状态:DONE - 变更:新增 `v1/alerts.py`:`annotate_frame` 用 OpenCV 在帧上叠加 box/骨架/关键点/标签,CONFIRMED 时加红色外框(红色专属确认摔倒);`EventArtifactWriter` 按 `artifacts/events/YYYYMMDD/FALL-xxxxxx.png` 保存带标注截图并向 `events.jsonl` 追加一行含 `event_id/track_id/config_version/source_id/state/confirmed_at_utc/单调时间/latency/相对截图路径` 的记录;`AlertDispatcher` 按 `event_id` 去重,对首次 CONFIRMED 只触发一次副作用。声音与弹窗抽象为 `AlertSink`;`v1/gui.py` 新增 `QtAlertSink`(`QApplication.beep` + 一次性非阻塞“我已知晓”弹窗),`v1/app.py` 在开始监控时构建 writer/dispatcher 并在每帧派发 `analysis.events`,窗口仍只渲染。 - 验证:新增 `v1/tests/test_alerts.py` 5 项,覆盖同一事件只写一次截图/JSONL 且 sink 各调用一次、JSONL 字段正确且无 rtsp/password、缺图不产生副作用、不同事件写两行、annotate 仅对 CONFIRMED 画红框;`python3 -m pytest v1/tests -q` 为 44 passed;`python3 -m compileall -q v1 demo` 退出码 0。真实帧冒烟:用 `demo/1.mp4` 首帧(848×480)经 `AlertDispatcher` 落盘,截图可被 `cv2.imread` 读回为 480×848×3、约 330 KB,重复派发返回 0,JSONL 一行且 `screenshot=20260721/FALL-000001.png`、含 `config_version`、`source_id`,行内无 `rtsp`。 - 阻塞:无任务内 blocker。声音播放与 Qt 弹窗需 Windows + PyQt5 桌面执行冒烟;本环境已真实验证截图标注、JSONL 落盘与去重逻辑。 - 决策:事件副作用严格按 `event_id` 幂等;报警记录以 `FallEvent` 的 `config_version` 追溯运行配置,不在写入时重新推导;工件文件名只用事件 ID 与日期目录,绝不含来源地址、凭证或客户姓名。 - 下一步:T-203,接入海康 RTSP 与断流恢复;因需现场真实流,标为 BLOCKED 等现场资源。 ## 【2026-07-21】T-203/T-204/T-205 现场依赖任务受阻 - 状态:BLOCKED - 变更:仅更新任务看板状态与本记录;未改动代码。按用户确认,Phase 2 剩余三项因现场物理资源不可得而受阻,不伪造 DONE。 - T-203 接入海康 RTSP 与断流恢复:断流状态、有界指数退避重连、STREAM 模式收帧单调计时与“断流不报警”已在 `v1/video_source.py` 与 `v1/pipeline.py` 实现并有单元测试;唯一未满足的验收是“现场有效海康流可预览”,需现场摄像头/网络与真实 RTSP 凭证(仅经环境变量提供)。 - T-204 建立正反例录像回归与现场验收记录:需经同意成年人的安全模拟摔倒录像与行走、坐下、弯腰、捡物、持续倒地等反例录像,以及现场安全措施;据此建立 `testdata/expected_events.json` 期望事件与 1–3 秒延迟记录。当前环境无这些录像素材,无法产出真实事件级验收。 - T-205 固化客户演示脚本与 V1 发布包:依赖 T-204 的现场验收证据,前置未满足,无法固化。 - 阻塞:现场海康摄像头、网络与真实 RTSP 凭证;经同意成年人的摔倒/反例录像与现场安全措施。决策人:用户/现场负责人。 - 决策:不以合成数据冒充现场 RTSP 预览或摔倒/反例录像验收(AGENTS.md:任务完成的证据是可运行命令与可观察结果,安全模拟摔倒必须使用经同意成年人与现场安全措施)。代码侧的重连、状态与幂等逻辑已就绪,解除阻塞后可直接进入现场验证。 - 下一步:待用户提供现场流/录像后领取 T-203;期间可按需在 Windows 执行 T-201/T-202 的 GUI、声音与弹窗可视化冒烟。 ## 【2026-07-21】FIX T-201/T-202 复核问题 A/B/C - 状态:DONE - 变更:修复三处复核问题。A:`PoseAdapter` 新增 `set_confidence_threshold`/`confidence_threshold`,`app.py` 在开始监控时按运行配置调用,使设置里的模型检测置信度真正生效(此前 adapter 置信度构造后固定、草稿字段为死旋钮)。B:`config.py` 新增显式 `source.mode`(`stream` 默认/`replay`,校验取值),`AppConfig.source_mode` 由配置决定,`app.py` 不再按 `source_url.startswith("rtsp")` 猜测来源模式;`config.example.json` 与 `docs/api.md` 同步。C:`FallStateMachine` 新增 `session_id`,`event_id` 变为 `FALL--NNNNNN`;`FallPipeline.from_config` 每次运行经 `new_session_id()` 生成进程内唯一会话,避免同一天内重启监控时截图被覆盖、JSONL 出现同 id 不同内容。 - 验证:新增/更新测试——config 三项(默认 stream、解析 replay、非法 mode 被拒)、pose 两项(置信度转发到推理、越界被拒)、fall_state 一项(不同会话 event_id 唯一)、pipeline 一项(两次 from_config 的确认事件 id 不同);`python3 -m pytest v1/tests -q` 为 51 passed;`python3 -m compileall -q v1 demo` 退出码 0。 - 阻塞:无。声音/弹窗/GUI 可视化冒烟仍需 Windows。 - 决策:来源模式由配置显式声明而非猜测;模型置信度经适配器方法在下次启动生效,保持“运行配置快照”语义;事件身份以会话前缀保证跨轮唯一,dedup 基于该持久标识。 - 下一步:T-203(受阻,等现场流)。 ## 【2026-07-21】T-203 接入海康 RTSP 与断流恢复 - 状态:DOING - 变更:用户提供真实海康 IPC 流(凭证与内网地址仅经 `SILVER_POSE_RTSP_URL` 环境变量提供,未写入任何被跟踪文件、日志或文档)。更新未跟踪的 `v1/config.local.json`(已被 `git check-ignore` 确认忽略):`source.mode=stream`、模型指向 `../demo/best.pt` 及其真实 SHA-256 `cd200948…bfafdfc`、只声明 `rtsp_url_env` 不含地址。 - 验证:在 WSL 以 TCP 传输 + 5 秒超时对真实流冒烟:`VideoSource(STREAM)` 首读即 `connected` 并解出 1920×1080 帧;持续取 5 帧、时间戳单调、跨度约 2.6 秒;对不可达地址进入 `retrying`、不崩溃、返回空帧且不报警。`load_config('v1/config.local.json')` 端到端通过:`source_mode=stream`、URL 从环境变量解析(不打印)、模型哈希匹配、`config_version` 生成。`python3 -m pytest v1/tests -q` 仍为 51 passed。 - 阻塞:模型前向(Ultralytics 8.3.205)与 PyQt5 预览在当前 WSL 不可运行,因此“带姿态骨架的操作员现场预览”与“真实断流→重连的现场观察”必须在 Windows 上完成;这两项完成前 T-203 不置 DONE。 - 决策:真实凭证只经环境变量注入,绝不入库;来源模式由 `config.local.json` 显式声明为 `stream`;本次相机密码含 `@` 字符,ffmpeg 以最后一个 `@` 分隔用户信息可正确解析,若换用其他客户端需将密码中的 `@` 百分号编码为 `%40`。 - 下一步:在 Windows 设置同名环境变量后运行 `python -m v1.app`,人工确认实时画面、骨架、ID、连接状态与断流重连;随后据现场结果置 T-203 状态。 ## 【2026-07-21】T-206 结构化摄像头配置、连接测试与设置页预览 - 状态:DOING - 变更:任务从 TODO 落为 DOING;尚未创建生产代码。按用户选择「全存本地配置文件」,凭证明文存于未跟踪 `config.local.json`。 - 验证:开始前 V1 单元测试 51 passed。 - 阻塞:设置页掩码输入、测试连接按钮与预览显示需 Windows + PyQt5 冒烟;配置解析、RTSP URL 构建(凭证百分号编码)与连接测试逻辑在本环境可真实验证。 - 决策:配置支持「结构化 host/port/channel/username/password」或原有 `rtsp_url_env` 二选一;密码按 RFC 3986 百分号编码后拼 URL(顺带解决 `@` 问题)。护栏:公开示例 `config.example.json` 继续禁止任何 host/账号/密码/URL 并加测试守卫;`runtime_config_version` 继续排除凭证;凭证绝不进 JSONL、日志、截图名、错误信息。 - 下一步:先写 URL 构建与结构化配置解析的失败测试,再实现;随后加 Qt 外壳(设置页字段、测试按钮、预览)。