创建人员:dev.developer.project.secondary / infodev-2
文件职责:说明 B 站博主动态采集重型本地协调工具的入口、输入输出、安全边界、运行方法和验收方法。
管理规范/模板:../../项目规范.md;../编码规范.md;../开发审计规范.md
引用文件:../../ai-video-downloader/draft/2026-08-04-哔哩哔哩动态采集需求.md;../../dev/project-dev/bili_dynamic_collector.py;../../dev/project-dev/bili_dynamic_collector.example.json;../开发事项总纲.md;../开发事项计划.md;../开发执行日志.md
记录方式:工具说明;公开 CLI、配置、状态、停止边界或验收结论变化时更新。
本工具是本地文件协调器,不是爬虫或浏览器机器人。它只读取人工或浏览器扩展已导出的无认证动态元数据,并完成以下工作:
manifest.jsonl。case_analysis.media_processor 的完整、内容寻址 Codex 原生任务 handoff envelope;工具本身不发送。工具不联网,不调用 B 站接口,不读取浏览器配置,不控制浏览器或扩展,不添加、重试或下载扩展任务,也不发送 Codex 消息。登录、验证码、会员、DRM、访问控制和来源合法性不明均由人工停止处理。
| 入口 | 路径 |
|---|---|
| CLI | dev/project-dev/bili_dynamic_collector.py |
| 开发示例配置 | dev/project-dev/bili_dynamic_collector.example.json |
| 测试 | dev/project-dev/test/test_bili_dynamic_collector.py |
| 脱敏 fixture | dev/project-dev/test/fixtures/bili_dynamic_collector/dynamic_items.json |
示例配置故意只指向 dev/project-dev/tmp/bili-dynamic-collector-mvp/。项目管理员完成正式输出路径授权扩展前,不得把开发运行改到 F:\video\<博主名>\ 或 ana-data/news-<博主名>/;本事项也不修改任何既有正式采集文件。
配置为 strict UTF-8 JSON。相对路径按配置文件所在目录解析。
{
"schema_version": 1,
"creator": {
"name": "青枫浦上Q",
"dynamic_url": "https://space.bilibili.com/1420210197/dynamic"
},
"timezone": "Asia/Shanghai",
"window_hours": 72,
"minimum_complete_age_seconds": 30,
"title_max_length": 48,
"allowed_source_hosts": ["space.bilibili.com", "www.bilibili.com"],
"allowed_video_extensions": [".mp4", ".mkv", ".mov", ".webm"],
"native_handoff": {
"project_id": "project-info",
"source_ai_id": "video-downloader",
"source_thread_id": "019fcc5d-798f-7ea1-8325-3a4d1f2dc5a5",
"source_role_instance_id": "case_analysis.video_downloader",
"target_ai_id": "media-processor",
"target_thread_id": "019fb7a4-bdfd-79f2-bd6b-e67e2b7d8efd",
"target_role_instance_id": "case_analysis.media_processor",
"reply_thread_id": "019fcc5d-798f-7ea1-8325-3a4d1f2dc5a5"
},
"paths": {
"state_dir": "tmp/bili-dynamic-collector-mvp/state",
"download_dir": "tmp/bili-dynamic-collector-mvp/downloads",
"video_dir": "tmp/bili-dynamic-collector-mvp/videos"
}
}
native_handoff 八个身份/路由字段全部必填,三个 thread ID 必须是小写 UUID。配置、导出、映射或历史 manifest 任意层级出现名称含 password、cookie、token、secret、authorization、captcha、session、密码、令牌、验证码或会话的字段时,三个命令都按 E_SECRET_FIELD 安全停止;只报告字段路径,不输出字段值,也不改写业务状态。
工具接收人工或扩展导出的 JSON 清单,不接收 HTML、浏览器 profile 或认证材料。
{
"schema_version": 1,
"creator": "青枫浦上Q",
"items": [
{
"dynamic_id": "123456",
"opus_id": "987654",
"bvid": null,
"content_type": "text",
"published_at": "2026-08-04T10:00:00+08:00",
"title": "短标题",
"source_url": "https://www.bilibili.com/opus/987654"
}
]
}
合同要求:
content_type 只能是 text、article、image、video。published_at 必须带 UTC offset 或 Z。dynamic_id、opus_id、bvid 至少一项存在;source_url 必须存在且 host 已登记。QUEUE_FAILED、MOVE_FAILED 或 PROCESSING_FAILED 时可重试,并追加新事件,不覆盖旧事件。python dev/project-dev/bili_dynamic_collector.py `
--config dev/project-dev/bili_dynamic_collector.example.json `
check `
--input <无认证动态导出.json>
check 是供 Codex 定时任务调用的入口。stdout 始终是一行 JSON;有新增时状态为 TODO_GENERATED 并给出待办路径,无新增时为 NO_NEW_ITEMS 且不新建空待办文件。--now <带 offset 的 ISO-8601> 只用于可重复测试或由调度器显式注入权威时钟。
待办动作只描述人工下一步:文字/专栏导出完整 UTF-8 文本、图片下载原图、视频打开播放页并使用已安装扩展。工具不会替代这些交互。
文件 stem 为 YYYYMMDD-HHmmss_<类型>_<短标题>。标题会替换 Windows 非法字符、去除尾部空格/点、保护保留名并按配置截断;不同实体占用同一 stem 时依次加 _01、_02。图片待办另给出同一动态多图的 _{sequence:02d} 命名模式。
state_dir/manifest.jsonl 是 append-only 事件账。每行至少包含:博主、动态/opus/BV 标识、内容类型、发布时间、标题、来源 URL、稳定去重键、建议 stem、本地文件、SHA-256、采集时间、状态、失败原因、视频处理状态和文本路径。每行 JSON parse 后会先经过与配置/输入相同的递归秘密字段门禁,再读取 schema、身份或状态。写入采用同目录临时文件、flush/fsync 和原子替换;重跑不改写历史事件。
视频终态还执行状态感知证据校验:VIDEO_MOVED/COMPLETE 必须有无控制字符的非空 local_file 和 64 位小写十六进制 SHA-256;VIDEO_MOVED_SOURCE_RETAINED 还必须有非空、无控制字符的 failure_reason。缺失、空白或格式损坏统一以 E_MANIFEST/exit 3 停止,不投影成功且不改写任何业务文件。
MVP 状态主链为:
TODO_QUEUED
-> MOVE_FAILED -> TODO_QUEUED(显式重试)
-> VIDEO_MOVED / VIDEO_MOVED_SOURCE_RETAINED
-> READY_FOR_HANDOFF(video_processing_status)
先由人工确认浏览器扩展任务已完成,再准备映射:
{
"schema_version": 1,
"items": [
{
"source_file": "扩展下载结果.mp4",
"bvid": "BV1xxxxxxxxxx"
}
]
}
source_file 相对路径按 download_dir 解析;也可给绝对路径,但解析后仍必须位于该目录。每项可同时提供动态 ID、opus ID、BV 号和来源 URL;每个已提供选择器都必须独立、唯一命中,且全部命中同一实体,未知或不一致的选择器不会被忽略。
python dev/project-dev/bili_dynamic_collector.py `
--config <已授权配置.json> `
move-completed `
--mapping <已完成视频映射.json>
移动采用两阶段全批预检。阶段 A 先解析全部选择器、拒绝重复实体并读取最新状态;只有最新状态精确为 TODO_QUEUED 或 MOVE_FAILED 的活动行进入阶段 B。处理阶段、缺失/空白或未知状态在复制前以 E_STATUS 停止。阶段 B 才检查临时下载后缀、完成宽限时间、视频后缀、下载根、链接/junction/reparse、物理同源和目标唯一性。任一预检失败时,整批不创建目标、不删除源、不追加 manifest。完整预检通过后的跨盘移动采用“目标目录隐藏 partial → flush/fsync → SHA-256 复验 → 同目录 hard-link no-overwrite 提交 → manifest → 删除源”的顺序;文件系统不支持安全 no-overwrite hard-link 时停止,不退化为可能覆盖的移动。
源删除失败时,目标和 SHA-256 仍可信,manifest 追加 VIDEO_MOVED_SOURCE_RETAINED,stdout exit 为 4,由人工核对并清理源副本。相同 mapping 重跑继续复用已记录的目标、hash 和失败原因并返回 exit 4;即使保留源后来被人工删除也不降级、不追加事件。正常 VIDEO_MOVED 重跑在源已删除后返回 ALREADY_MOVED/0。正式目标绝不覆盖。
python dev/project-dev/bili_dynamic_collector.py `
--config <已授权配置.json> `
handoff
工具会先逐个确认视频仍在 video_dir 内、不是链接且 SHA-256 与 manifest 一致,然后在 state_dir/handoffs/ 生成内容寻址的完整 <codex_native_handoff> Markdown。正文含精确 source/target AI、thread、role、reply thread、scope/evidence/expected_action,状态为 PROCESSING_REQUESTED;handoff ID 由路由和排序后的证据派生。同一路由/证据即使 --now 不同也得到相同 bytes 并返回 REUSED。stdout 始终 sent=false;文件生成不等于发送,仍由视频下载员通过 Codex 原生任务通信确认并发送。
生成前会验证全部 handoff 消费标量:内部 entity ID、可选 BV、canonical UTC 发布时间、标题、来源 URL、本地路径及 SHA-256。CR/LF、其他控制字符或格式异常均在文件创建前以 E_MANIFEST 停止;错误输出只含安全字段路径,不回显污染值。
| exit | 含义 |
|---|---|
0 |
成功或无新增 |
1 |
未预期内部失败,默认关闭 |
2 |
输入/配置合同错误 |
3 |
安全停止:路径、认证字段、重名、未完成、身份冲突等 |
4 |
目标已安全提交,但源文件删除失败并保留 |
130 |
用户中断 |
登录提示、验证码、付费墙、会员异常、DRM、访问控制、来源合法性不明和浏览器扩展任务异常不由本 CLI 处理;人工应停止并报告,不得向配置或输入补入认证秘密。
后继事项 DEV-PROJECT-INFO-BILI-DYNAMIC-REFRESH-COLLECTOR-20260813-001 的 runtime-v2 唯一产品入口为:
python -B dev/project-dev/bili_dynamic_collector.py `
--config dev/project-dev/bili_dynamic_collector.example.json `
refresh-run
refresh-run 由受信 controller 独占一次性密钥、deadline、action/evaluate dispatch、source-controlled extractor、evidence 签名与提交。其标准输入/输出是严格 JSONL 的 supported-Chrome 请求/响应协议;调用方不能读取 capability、注入自定义 adapter、手写 schema 3 evidence 或直接绑定成功终态。refresh-begin/refresh-commit 仅保留历史 schema 1/2 恢复边界,新 schema 3 run 不能从这两个公开入口创建。工具不读取、导出或持久化 Cookie、token、localStorage,也不提供 Raw CDP、坐标或截图降级。刷新身份固定为 青枫浦上Q / 1420210197 / https://space.bilibili.com/1420210197/dynamic,每个 run 最多刷新一次。
页面 observation 合同与只读提取脚本以精确 hash 绑定。只有页面身份、正文、72 小时时间窗与终止标记共同证明完整覆盖时,空结果才可成为 REFRESH_CONFIRMED_NO_NEW。公开搜索/API 的空结果、-412、验证码、访问限制和不可读页面不能证明 no-new,分别落入 REFRESH_FAILED_PAGE_UNREADABLE、REFRESH_BLOCKED_AUTH_OR_ACCESS 或 PARTIAL_DISCOVERY_UNCONFIRMED。发现通过合同的新正文、原图或视频 metadata 时才产生 NEW_ITEMS_SAVED。
state lock 使用稳定 kernel-backed 文件;正式 manifest 锁使用进程创建 identity、archive 同目录同卷 quarantine、generation 与 claim attempt。小时运行以 168 个 UTC slot 有界保存;state/formal 两份 manifest 通过 pending phase 和正式锁一致提交,锁、前像、路径、hash、并发或恢复有歧义时安全停止。
开发测试只重放脱敏状态:2026-08-13 既有一次 reload 后 15 秒页面边界和 8 秒只读 evaluate 超时,固定为 REFRESH_FAILED_PAGE_UNREADABLE;无凭据公开 endpoint 的 code=-412 仅作非权威 partial/access 附证。本实现没有再次刷新、联网、下载或写正式 ana-data。完整实现与验证证据见 B站动态主动刷新采集器实现与验证证据.md。
python -m py_compile dev/project-dev/bili_dynamic_collector.py
python dev/project-dev/bili_dynamic_collector.py --help
python -m unittest dev.project-dev.test.test_bili_dynamic_collector -v
目标测试包含 13 个离线用例,覆盖选择器一致性、同源/同实体全批预检、三类终态重跑、六类禁止状态、terminal 缺失/空白/格式损坏矩阵、handoff 全标量控制字符/格式注入矩阵、manifest secret gate、规范 handoff 稳定性及原有窗口/命名/移动边界。目标 13/13、新鲜闭环 1/1、project 76/76 均 PASS;审核员另独立复跑原阻断反例 2/2、目标 13/13、project 76/76,限定实现复审结论为 PASS/0。测试只使用脱敏 JSON 和临时小字节文件,不联网、不启动浏览器、不读写 F:\video\、不读写正式 ana-data,也不复制博主正文或真实媒体。请求方已按 HANDOFF-VIDEO-DOWNLOADER-INFODEV2-BILI-DYNAMIC-COLLECTOR-PASS0-ACK-20260805-001 接收并关闭本开发事项;真实网络、浏览器、下载、自动 native send、每小时调度、F:\video\青枫浦上Q 和正式 ana-data 的既有授权由视频下载员按项目配置消费,本工具和本开发事项本身不构成这些动作的授权来源。
主动刷新产品入口使用 refresh-run,并由产品级 JsonLineSupportedChromeAdapter 执行 controller→adapter→extractor→evidence→commit 单一路径。受信 key 只存在于 controller 进程内,pending 仅保存 SHA-256 commitment;产品模块不暴露签名/绑定 helper,任意调用方 adapter 在动作前以 E_CONTROLLER_ADAPTER 停止。可读页面的 observation cards 必须与提交 items 精确双向绑定;缺项、额外项、跨卡身份重叠或字段漂移均失败关闭,不能被当作无新增。历史目录会跨 schema 1/2 全量归并身份组件,并对状态优先级和 schema 2 artifact path/bytes/SHA-256 做严格验证。
每个小时只写一个不可变终态 slot;latest.json 只是精确八键索引,可从已验证 slot 重建。无正式变化也必须形成 NO_FORMAL_CHANGE receipt。崩溃恢复会验证 pending 阶段、证据身份、formal-lock generation/attempt、同卷 quarantine owner marker 和 durable slot/latest readback;歧义状态不会写正式 manifest。HOLD/4 修复后的离线目标测试为 collector 13 项 + refresh 17 项,共 30/30 PASS。本能力仍不构成 Chrome、网络、正式采集或计划任务的运行授权。
HOLD/3 修复进一步冻结:schema 2 UID 必须是精确 string,schema 1 数字 stable ID 只允许 1—32 位 ASCII;pending→STARTED 崩溃只重建同 run,slot 第三内容不覆盖,TERMINAL_RECORDED 必须重新绑定 planned/receipt/manifest/slot。formal-lock 隔离采用 pending intent 先行、no-overwrite rename、目录 durable flush,并能在主锁已先删除后继续 exact cleanup。当前离线目标回归为 34/34 PASS。
runtime HOLD/3 后继修复冻结:新 schema 3 pending 的原始 deadline_at 必须在任何 adapter/open-tabs/reload/goto/evaluate 调用前校验,过期即 E_OVERALL_DEADLINE,协议请求数与 evidence 文件数均为 0;deadline 不得重置。五态产品测试通过真实 refresh-run 子进程和严格 JSONL transcript 覆盖 new/empty/unparsed/identity/access,不使用 Fake adapter。当前 refresh=39/39 PASS,collector+refresh=52/52 PASS;正式 manifest 保持 51598 bytes / 48 lines / 0E69AF2AF1C4B0014D2033A21F3986E283ECFD758EA652E7F2416006736C8D73。
调用方 JSONL 不再是可信运行入口。refresh-run 现在固定在创建 pending 或读取 stdin 前返回 E_TRUSTED_ADAPTER_REQUIRED/SAFETY_STOP/exit3,并明确 authoritative=false,saved=false,no_new=false。任何公开 index、fixture、caller transcript、unpacked 自报 ID/hash/enabled 或旧 evidence 都不能形成保存或 no-new。
离线实现位于 dev/project-dev/bili_dynamic_refresh_extension/ 与 dev/project-dev/bili_dynamic_refresh_native_host/。扩展是独立 cookie-free MV3,固定 ID=gllihoanalkiollgpeggamfhajmmnmml;Host=com.project_info.bili_dynamic_refresh,allowed origin 只有该 ID。源树、reviewer source approval、账户持有人在 Chrome 可见界面手工加载形成的 local-load approval、Host install receipt 和当前 Chrome 实例必须同时精确匹配。unpacked 路线是当前本机单用户的程序性信任,不等同于签名分发,也不声称抵御已控制本机账户的恶意进程。
当前只完成源代码与离线合成测试。没有正式 source/load approval,没有 build/install/HKCU,没有打开 Chrome 或加载扩展。永久不使用 CWS upload/publish、ExtensionInstallForcelist、ExtensionSettings 或 Chrome policy;后续 Host 注册若获批也必须是独立 CreateNew、默认不执行、可回滚门禁,且不得修改完整视频扩展/Host。
当前周期入口为 dev/project-dev/bili_half_hour_pipeline.py 与受管配置 dev/project-dev/bili_half_hour_pipeline.config.json。固定 creator 是 青枫浦上Q / 1420210197 / https://space.bilibili.com/1420210197/dynamic,固定周期 30 分钟;协调器本身不读取 Cookie、Profile、localStorage 或网络载荷,而由已登录 Chrome 的受控运行层完成页面刷新和采集,再读取现有 append-only formal/handoff 事实。旧第 10—13 节的小时/V009 离线入口仍作为历史合同与兼容实现保留,不再表示当前调度频率。
每轮先 initialize(首次只建立当前 formal/handoff baseline),再 begin、页面刷新/采集、reconcile、outbox 分发与 finish。state 用非阻塞进程锁和确定性 half-hour slot 防重叠;cursor 同时冻结行数与完整前缀 SHA-256,历史重写、截断或并发漂移均 fail-closed。正文/原图形成 GIT_DELIVERY_READY,未完成视频形成 VIDEO_DOWNLOAD_READY,新的 processing READY 形成 VIDEO_TRANSCRIPTION_READY;转写 receipt 再形成 MINUTES_READY 与 transcript Git outbox,纪要 receipt 形成 minutes Git outbox。每个 outbox ID 同时绑定 source 与规范 payload;任何读取、派发、回执和 Git 消费都会从 exact formal/handoff/terminal 行重新投影并核对。角色状态严格单向为 CREATED→DISPATCH_INTENT→OBSERVED→COMPLETE,terminal 不得早于 observed,重启只恢复同一 envelope,不制造第二次 handoff。
输出布局保持:正文、原图、manifest、转写、纪要在 ana-data/news-青枫浦上Q/;MKV 和 .download.json 只在 F:\video\青枫浦上Q\。Git 发布拒绝预存 staged changes;受管根、完整祖先链和最终文件均须 ordinary/non-reparse、严格解析包含,并在读/hash/commit 边界复核身份。文件内容由安全句柄读入后直接写入 Git blob,再通过任务自有临时 index 形成 exact tree;共享 index、无关 dirty changes在所有异常/进程中断窗口保持不变。持久 GIT_COMMIT_INTENT 绑定 parent/tree/files/message/author/time,重启确定性恢复 non-force push;禁止 git add .、空提交、force push、原视频/mapping/秘密入库。已登记 job/config 是连续执行授权,正常心跳不得逐轮索权。
操作员可见状态区分:RUN_STARTED/RUN_RESUMED、RECONCILED、待分发 outbox、下游 terminal receipt、RUN_COMPLETE/RUN_FAILED。无新增仍须完成本轮并保留 cursor;有新增必须等相应 outbox/回执闭环,媒体已下载但后处理待补时继续沿分层结果报告,禁止重下载。
正文、专栏、原图、视频转写和纪要统一采用 YYYYMMDD-HHMMSS_<content-kind>_<sanitized-title>_<stable-id>。视频的 transcript/minutes 分别放在同 basename 的 .transcript/、.minutes/ 目录,目录内文件也使用同 basename。标题按 Windows 规则确定性清洗并限制 64 字符;时间和标题只来自冻结的 formal/mapping,BVID 始终为末尾稳定 ID。MKV、.download.json 和 FLAC 仍使用 BVID 内部名,分别留在视频根和 intermediate/transcription/,永不进入 Git。
plan-video-artifact-migration 只读生成确定性迁移计划;migrate-video-artifacts 先 CreateNew 内容寻址 report,再逐文件验证字节/SHA、无覆盖迁移并建立 Git outbox。artifact-relocations/<batch>.json 是不可变别名账本,历史 formal、terminal、receipt 不重写;旧路径缺失时,resolver 只有在 old/new/bytes/SHA 唯一绑定后才读取新路径。公开文件迁移提交同时精确删除旧 Git 路径,不能把新旧副本并存。
git-preflight 是只读入口:冻结 .git/index 的字节、SHA 和文件身份,比较当前 HEAD 的 staged path,报告 archive 范围“staged delete + worktree present”以及 .git/index.lock 身份。无论成功、失败或中断,共享 index 必须字节与文件身份不变;工具不执行 reset、checkout、add 或 lock 清理。交付继续使用任务临时 GIT_INDEX_FILE,共享 index 的用户原始 staged 集不能因 HEAD 移动而被重新解释或改写。
python -B dev/project-dev/bili_half_hour_pipeline.py --config dev/project-dev/bili_half_hour_pipeline.config.json git-preflight
python -B dev/project-dev/bili_half_hour_pipeline.py --config dev/project-dev/bili_half_hour_pipeline.config.json plan-video-artifact-migration
python -B dev/project-dev/bili_half_hour_pipeline.py --config dev/project-dev/bili_half_hour_pipeline.config.json migrate-video-artifacts
半小时失败若没有可验证 Host terminal,必须记为失败而非 no-new。恢复时保持同一 task/owner/reviewer,先解决受信 Host/浏览器生命周期问题,再从最后 accepted cursor 补采;每个 slot 仍只刷新一次。Codex Desktop signed bundle 的 Stdio write EOF 竞态属于上游客户端边界,项目侧只移除 recurring node_repl/globalThis 触发并固定脱敏 E_NATIVE_PEER_CLOSED,不能宣称已永久修复客户端。
半小时页面刷新不再由 Codex 自动化通过持久 node_repl、globalThis 标签页对象或交互 Chrome 工具执行。独立 cookie-free MV3 扩展使用 chrome.alarms 在半小时边界触发,并通过短生命周期 Native Messaging port 完成握手、唯一刷新、观察和 Host 提交;Codex 自动化仅处理源控协调器和下游 handoff,不拥有浏览器 transport。
每个槽位只创建一个 inactive 的任务自有标签页。标签页以 slot/lease 页面标记和 chrome.storage.session 记录双重绑定;跨 dynamic/opus 导航时,document-start content script 只能向本扩展 service worker 请求经 tab/window/URL/lease 重绑的标记,不能直接读取 session storage。Host commit 完成或失败/finally 后,扩展重新核对 tab/window/URL/标记,先退休持久所有权,再唯一执行一次 tabs.remove。已关闭是幂等成功;对象漂移、关闭失败只记录固定脱敏状态,禁止导航到 about:blank、禁止第二次 remove/update,也绝不关闭用户已有标签页或共享窗口。
Native port 在首次消息前安装 message/disconnect handler,写入按序执行;peer 在写前、写中、写后关闭统一为 E_NATIVE_PEER_CLOSED。Python Host 将单个 frame 合并为一次受互斥保护的写和一次 flush,只吞并确定的 pipe peer-close,其他 I/O 错误继续失败。连续 100 个槽位、service-worker 重启、同 ID 用户替换、关闭失败和 peer-close 竞态均由生产形状测试覆盖。
本节只消除本项目制造的重复 stdio/tab 触发,不能修改已签名 Codex 桌面客户端的主进程写竞态。自动化 q 必须保持暂停,直到源审 PASS/0、可验证部署以及受控客户端重启清除旧管道;若可信扩展/Host 尚未可验证,返回固定 E_TRUSTED_ADAPTER_UNAVAILABLE,不得回退到旧交互循环。脱敏复现和上游边界见 Codex桌面Stdio写EOF脱敏复现与项目侧缓解.md。
bili_dynamic_refresh_extension 1.1 与 com.project_info.bili_dynamic_refresh 不再使用固定 E_TRUSTED_ADAPTER_UNAVAILABLE stub。扩展在已登录的 allowlisted dynamic/opus 页面只读取可见 DOM:creator UID/name、规范页面身份、完整正文、公开原图候选、BVID、发布时间和页面可见时长;它不读取或传输 Cookie、token、signed URL、localStorage、Chrome Profile、验证码内容或原始网络记录。访问控制、owner 不匹配、未解析卡片、正文/原图未稳定、聚合 bundle 超过 900000 bytes 或字段含秘密形状时均 fail-closed,不能形成 no-new。
Host 通过 Native Messaging exact origin、reviewed local-unpacked source identity、slot/lease 和 HMAC 序列绑定一次 capture。公开原图仅允许无 query/fragment 的 i0/i1/i2.hdslb.com/bfs/ 原图 URL,下载器禁用代理继承、限制重定向/字节/魔数,以 CreateNew 临时 intake 交给既有 bili_article_image_collector.py;正文/原图由该收集器按 formal append-only 保存,视频只追加规范 BVID metadata,再由 bili_half_hour_pipeline.py 投影现有 exact-once outbox。capture bundle 和原图 URL不进入 durable terminal;终端只保留 slot、run、capture SHA、数量和 outbox ID。相同 slot 的 durable terminal replay 在 Native/tab/page action 前返回。
构建/安装入口为 bili_dynamic_refresh_native_host/build_host.ps1 与 install_native_host.ps1。构建只接受固定离线 Python/PyInstaller 6.15.0/provision identity 和原 reviewer 一次合并 PASS receipt,精确绑定 extension、Host、article collector、half-hour pipeline 四套 source manifest;安装复用同一 PASS receipt,不再制造 build-hash 二次复审门。安装默认仅 ValidateOnly,-Install 只向缺失的版本目录 CreateNew 写 EXE、runtime config、native manifest、install receipt 与由可见 unpacked-load 事实机械生成的 identity facts,并在缺失的 HKCU\Software\Google\Chrome\NativeMessagingHosts\com.project_info.bili_dynamic_refresh 注册默认 String。扩展继续采用 Chrome 可见界面的本机 unpacked 加载,不使用 CWS、policy、Raw CDP 或 Profile 写入。
扩展安装/重载本身只恢复 durable slot 并建立 30 分钟 alarm,不会在 Host 身份材料尚未完成时抢先采集。一次真实部署回填通过扩展 action 的 kickoff.html 明确触发 RUN_CURRENT_SLOT_REQUEST;消息只能来自同扩展的精确页面 URL,并仍进入同一个 FIFO、durable STARTED/terminal 与 exact-once 协调器。它不是逐轮许可入口,日常后续轮次仍完全由 alarm 自动执行。
当前 source tests 已覆盖非空图文+原图+视频、零新增重放、malformed/secret/owner mismatch、Host 已许可重启拒绝、peer-close、100 槽位生命周期、受控当前时槽入口和任务自有 tab 关闭。此节记录的是待复审 source 状态;在原 reviewer PASS/0、一次受控 build/install/reload、真实从最后 accepted cursor 回补并得到当前 RUN_COMPLETE 前,q 仍保持暂停。项目侧部署只能移除高频触发,不能永久修复已签名 Codex Desktop 的主进程 pending WriteWrap/EOF 竞态。
Host 在正式 run/outbox/terminal journal 之前另有固定 schema 的 append-only phase ledger,用于区分进程入口、运行配置、本地身份、Chrome launcher、首帧、maintenance、slot HELLO、durable replay/resume、session 与 durable handoff 边界。每行只包含 UTC 100ns 时间、build/session nonce 的 SHA-256、固定 phase、固定错误码,以及 PID/进程创建 FILETIME;禁止写入页面 URL/标题/正文/capture payload、Cookie/header/token/signed URL、localStorage/Profile、命令行、原始异常或任意调用方字符串。未知错误一律收敛为 E_HOST_INTERNAL。
ledger 位于已审核 Host 的唯一版本安装根、与 runtime-config.json 同目录,因而在配置解析失败前仍可用。该版本稳定态只拥有一个固定 preterminal-phase-ledger-v1.jsonl 和一个固定 lock;JSONL 最多 131072 bytes、512 条诊断事件,达到任一上限后停止追加并固定返回 CAPACITY_REACHED,已有字节 mutation0,业务 run 继续执行;永不轮转为多段,也不删除、替换、覆盖正式 ledger。首次初始化使用唯一固定 .initializing CreateNew 暂存对象:Windows 内核在 OWNER 首记录 durable retain 前负责 crash-close 自动回收;OWNER 首记录严格绑定当前 build/session hash 与该 held 文件的 volume/file identity,fsync 后才取消自动回收,再以 no-replace 原子发布为正式 ledger。若崩溃发生在 retain 后、发布前,重启只恢复 OWNER 记录与当前暂存对象身份完全一致的对象;若发生在发布后、lock owner receipt 完成前,重启只接受正式 ledger 的同一 OWNER 身份,并只修复空或其 canonical receipt 前缀。未知暂存、目标、receipt 或身份漂移保持原字节并失败关闭。初始化成功后 .initializing 必须不存在。两个受信 Chrome parent 通过同一 held lock 串行,Host 生命周期内持续持有根目录、lock 与 JSONL 句柄;每行 append 后立即 flush/fsync,并从同一 held handle 精确读回完整前缀和新行,不能只比较路径或大小。锁内先验证 OWNER 首记录和全部 canonical JSONL;仅允许截除 OWNER 后没有终止换行的撕裂事件尾部,完整但非法的行一律失败关闭,随后才可 append。任何 trace、回读、对象身份、PROCESS_EXIT 或 close 失败均由最外层非递归边界固定收敛为 HOST_FATAL/E_PRETERMINAL_LEDGER,不输出原始异常;maintenance/replay/session 的写端 peer-close 和 ACK 前 EOF则保留 PROCESS_EXIT/E_NATIVE_PEER_CLOSED,不得被误写为 OK 或 E_PROTOCOL。该 ledger 只用于诊断早退,不替代 formal terminal,也不能把失败解释为 no-new。