edit | blame | history | raw

慧博 10 分钟快速采集工具开发方案 V002

创建人员:dev.developer.ana.cai
文件职责:append-only 修复 V001 独立审核的 6 个 blocking findings,并冻结可直接实现、测试和复核的模块 API、状态模型、时间边界、原子归档、额度并发与性能样本合同。
predecessor:CODE-DESIGN-ANA-HIBOR-FAST-COLLECTION-V001=15549/ecacaaea740454ae56e928ef58af4a5d5e706334b1ac917c8647db8a8eb3edf9
predecessor review:AUDIT-DEV-ANA-HIBOR-FAST-COLLECTION-DESIGN-V001=HOLD/6/6,审计入口 ana-doc/案例审计报告.md
记录方式:V001 保持不可变;V002 对冲突处拥有后继优先级,V001 已通过的包名、缓存、访问控制、原件、额度上限、角色和零实现关口全部继续有效。普通源码/内部测试按语义验收,PDF、正式 manifest、额度账本和终态证据按真实消费者要求做哈希与不可变验证,不恢复普遍逐字节预冻结。

1. 本轮 closure 与不变边界

V002 必须同时关闭:

  1. BLOCK-HIBOR-DESIGN-01:逐模块 API、调用图、canonical model/schema、主键、错误与 exit 总映射;
  2. BLOCK-HIBOR-DESIGN-02:任务观察时点传递、单一 monotonic deadline、子进程/锁/I/O/terminal 的有界生命周期;
  3. BLOCK-HIBOR-DESIGN-03PROVISIONAL_VISIBLEDETAIL_CONFIRMED 分离,早停失效后的 cursor 恢复与补扫;
  4. BLOCK-HIBOR-DESIGN-04:同目录 staging、完整验证后原子 no-replace 发布、崩溃恢复与 package closure;
  5. BLOCK-HIBOR-DESIGN-05:额度 read-fold-reserve 原子临界区、事件主键/幂等、并发和 replay;
  6. BLOCK-HIBOR-DESIGN-06:10 个 trigger 的预登记总体、批量计数、SLA failure 与 external exclusion 口径。

方案复审 PASS 前仍禁止创建候选源码/测试、运行 Python/ADB/APP/dry-run、创建额度账本或产生真实下载 trigger。

2. 唯一 owner、模块 API 与调用图

2.1 唯一 owner

cli.main() 是唯一 orchestration owner;其余模块不得互相创建 run、写 terminal、追加额度或自行启动外部命令。RunContext 由 CLI 创建一次并只读传递。外部命令全部通过 ProcessSupervisor.run(argv: Sequence[str], ...),必须是 argv list、shell=False;模块不得调用 os.system、shell 字符串、隐式 PATH 脚本或自行 subprocess.*

2.2 公共 API

下列为包内唯一 public API;未列函数/类必须以下划线开头,测试不得依赖其实现细节。

模块 唯一公共 API 返回/异常 owner
models.py load_task_spec(path: Path) -> TaskSpecvalidate_task_spec(x: Mapping) -> TaskSpecto_ordered_dict(model) -> dict 仅抛 ContractError(code, field, detail) CLI
budget.py Budget.start(observed_at_utc: str, total_ms: int, close_reserve_ms: int, clock: Clock) -> Budgetremaining_ms(kind: Literal['work','close']) -> intcheckpoint(phase: Phase, operation: str, kind='work') -> Noneslice(max_ms: int, teardown_ms: int) -> int 到期抛 BudgetExpired; wall-clock 回退/未来时间/启动前已耗尽抛 ContractError CLI
process.py ProcessSupervisor.run(argv: Sequence[str], cwd: Path, budget: Budget, phase: Phase, input_bytes: bytes|None, max_stdout: int, max_stderr: int) -> ProcessResult 不抛裸异常;所有可观测结果封装;liveness unknown 置 LIVENESS_UNKNOWN ADB/PDF provider only
adb.py AdbClient.preflight(ctx) -> DeviceSnapshotlist_cache(ctx) -> tuple[RemoteFile,...]remote_sha256(ctx,path)->strpull(ctx,remote,local_staging)->PullResultui_dump(ctx)->UiSnapshotscreenshot(ctx,dst_staging)->ArtifactDrafttap/swipe/input_text/back/start_package(ctx,...) -> ActionReceipt 只经 ProcessSupervisor;包/设备/cache 身份漂移为 typed stop UI/cache
ui.py FastScanner.scan(ctx, checkpoint: ScanCheckpoint|None) -> ScanOutcomeconfirm_detail(ctx,candidate)->Candidaterestore(ctx,checkpoint)->ScanCheckpointtrigger(ctx,candidate,reservation)->TriggerReceipt 不写 quota/PDF/terminal;每次动作前后验证 anchor/package CLI
quota.py QuotaLedger.snapshot(ctx)->QuotaSnapshotreserve(ctx, report_key, slot_id)->ReservationResultconfirm(ctx,reservation,trigger)->QuotaEventmark_uncertain(...)release(...)raise_baseline(ctx,floor,evidence_ref)->QuotaEvent 每次 API 内部独占一次完整 read-validate-fold-check-append-fsync-reopen 临界区 CLI
cache.py CacheWatcher.capture_baseline(ctx)->CacheBaselinewait_unique_stable(ctx,baseline,trigger)->CacheMatch 只读远端;歧义/超时返回 typed stop CLI
archive.py ArchiveManager.stage_pull(ctx,match)->ArtifactDraftvalidate(ctx,draft,detail)->ValidatedArtifactpublish_no_replace(ctx,validated,destination)->PublishedArtifactrecover(ctx,recovery_record)->RecoveryResult 不写 manifest/quota/terminal;正式路径只通过原子 publish 出现 CLI
manifests.py append_manifest(ctx,row)->ManifestReceiptwrite_delivery(ctx,package)->PublishedArtifactwrite_timing(ctx,rows)->PublishedArtifact 同目录 staging+验证+no-replace;manifest 单锁 append 并 fsync CLI
terminal.py build_terminal(ctx,package,stop)->TerminalRecordpersist_terminal(ctx,record)->TerminalReceiptrender_public_payload(record)->str 不发送消息;console payload 永远可生成,持久化失败如实记录 CLI
cli.py main(argv: Sequence[str]|None=None)->int 捕获所有 typed/untyped 错误,按第 5 节唯一映射返回 public

2.3 固定调用图

cli.main
  -> models.load_task_spec
  -> Budget.start
  -> AdbClient.preflight + QuotaLedger.snapshot
  -> FastScanner.scan
       -> AdbClient.ui_dump/screenshot/action
  -> FastScanner.confirm_detail
  -> CacheWatcher.capture_baseline
  -> QuotaLedger.reserve
  -> FastScanner.trigger
  -> QuotaLedger.confirm|mark_uncertain|release
  -> CacheWatcher.wait_unique_stable
  -> ArchiveManager.stage_pull -> validate -> publish_no_replace
  -> append_manifest
  -> write_delivery -> write_timing
  -> build_terminal -> persist_terminal -> render_public_payload

collect-batch 仅在 CLI 层循环 item;每个 item 使用独立 report_key、reservation、artifact 和 manifest row,共享 search/scan checkpoint。任何模块不得反向调用 CLI 或 terminal。

3. Canonical model 与 schema

3.1 基本类型与序列化

  • Id:非空 ASCII [A-Za-z0-9._:-]{1,160}PathText:绝对 Windows 路径字符串;RemotePath:以固定 cache root 开头的 POSIX 路径。
  • UtcTime:UTC RFC3339,毫秒 3 位,后缀 ZHash:小写 64 位十六进制;Bytes/Millis:非负 JSON integer;BOOL 只允许 JSON true/false。
  • JSON 使用 UTF-8、无 BOM;模型输出按本文字段顺序,null 必须显式出现,不允许额外键。CSV 列顺序固定,null 编码为空字段,BOOL 编码 true|false
  • canonical acceptance 是“严格 parse + 字段/类型/枚举/交叉约束 + 重序列化语义一致”;只有 quota/正式 manifest/terminal 发布后记录实际 bytes/hash,不预冻结未来运行字节。

3.2 TaskSpec V002(有序 27 键)

schema_version,task_id,handoff_id,requester,review_owner,mode,query,quantity,destination_root,output_root,quota_ledger,adb_executable,pdfinfo_executable,package_name,cache_root,device_serial,observed_at_utc,total_budget_ms,close_reserve_ms,batch_increment_budget_ms,min_screens,normal_max_screens,hard_max_screens,hard_max_candidates,selection_filters,performance_slot_id,performance_plan_id

  • schema_version='HIBOR_FAST_TASK_SPEC_V002'mode=DRY_RUN|COLLECT_ONE|COLLECT_BATCH|RESUME_POSTPROCESS
  • package_name='cn.com.hibor'cache_root='/sdcard/Android/data/cn.com.hibor/files/myfile/'
  • quantity INT 1..10;collect-onetotal_budget_ms=600000collect-batchtotal_budget_ms=600000+240000*(quantity-1)close_reserve_ms=10000batch_increment_budget_ms=240000;screens=3/5/20;candidates=100
  • device_serial STRING|null;dry-run 必须 null,真实模式必须非空并与唯一设备一致。
  • performance_slot_id/performance_plan_id 均 STRING|null;真实性能运行必须非空,普通生产采集均为 null。
  • selection_filters 为有序对象:subject:string,allowed_institutions:array<string>|null,report_types:array<string>,date_from:YYYY-MM-DD,date_to:YYYY-MM-DD,min_pages:int,max_pages:int,analyst_required:bool,max_per_institution:int;不得含评分之外的主观字段。

3.3 Candidate V002(有序 22 键)

candidate_id,report_key,state,query,screen_index,rank_on_screen,result_title,result_institution,result_date,result_pages,result_analysts,detail_title,detail_institution,detail_date,detail_pages,detail_analysts,detail_fingerprint,hard_filter_pass,score,duplicate_of,reject_code,checkpoint_id

  • state=DISCOVERED|PROVISIONAL_VISIBLE|DETAIL_OPENED|DETAIL_CONFIRMED|REJECTED|TRIGGER_RESERVED|TRIGGERED|CACHE_MATCHED|ARCHIVED|FAILED
  • result 字段在 PROVISIONAL_VISIBLE 后非空;detail 字段在 DETAIL_CONFIRMED 后非空;此前为 null。
  • hard_filter_pass 为 BOOL|null,仅 DETAIL_CONFIRMED/REJECTED 可非空;score 为 INT|null,仅 result 字段完整时可非空。
  • duplicate_of/reject_code 为 STRING|null;被拒/重复时必须提供其一。稳定 top-K 只能使用 PROVISIONAL_VISIBLE 的 provisional ranking 作为“暂停扫描”依据,最终 trigger 集只允许 DETAIL_CONFIRMED && hard_filter_pass=true && duplicate_of=null

3.4 ScanCheckpoint V002(有序 10 键)

checkpoint_id,query,screen_index,scroll_count,page_fingerprint,first_visible_key,last_visible_key,seen_candidate_ids,provisional_top_ids,created_at_utc

数组顺序为实际扫描顺序;page_fingerprint 是 UI dump 规范化可见节点的 SHA-256,不是截图内容推断。restore 后 query、screen/scroll、first/last key 和 fingerprint 必须全部一致,否则 UI_CURSOR_RESTORE_FAILED

3.5 QuotaEvent CSV V002(固定 18 列)

schema_version,event_id,idempotency_key,ledger_date,event_seq,event_type,task_id,handoff_id,run_id,slot_id,report_key,reservation_id,ref_event_id,confirmed_delta,uncertain_delta,reservation_delta,baseline_floor,created_at_utc

  • event_id=sha256(ledger_date+'|'+idempotency_key+'|'+event_type+'|'+event_seq)idempotency_key=sha256(task_id+'|'+handoff_id+'|'+slot_id+'|'+report_key)
  • event_type=BASELINE_ESTIMATE|CORRECTION_RAISE|RESERVE|CONSUME_CONFIRMED|CONSUME_UNCERTAIN|RELEASE
  • baseline/correction:三个 delta 为 0,baseline_floor 为 INT;baseline 首次至少 3,correction 只能提高 floor,ref_event_id 指向证据事件。
  • reserve:0/0/+1;confirmed:+1/0/-1;uncertain:0/+1/-1;release:0/0/-1;其余组合非法。
  • 一个 idempotency key 最多一个 active reservation 和一个终结事件;同 event_id/同整行是 replay,返回原 receipt;同 event_id 或同 idempotency key 但字段不同为 QUOTA_REPLAY_CONFLICT

3.6 TimingRow CSV V002(固定 12 列)

schema_version,run_id,item_id,phase,operation,monotonic_start_ns,monotonic_end_ns,elapsed_ms,wall_start_utc,wall_end_utc,outcome,detail_code

phase=preflight|quota|ui_search|ui_scan|detail_and_trigger|cache_wait|copy|validation|manifest|delivery|timing|terminaloutcome=PASS|STOP|FAIL|SKIP。monotonic 只允许进程内差值,wall 仅审计。

3.7 ManifestRow CSV V002(固定 34 列)

schema_version,row_id,task_id,handoff_id,run_id,item_id,slot_id,requester,review_owner,query,candidate_id,report_key,title,institution,report_date,analysts,selection_reason,remote_path,remote_bytes,remote_sha256,local_path,local_bytes,local_sha256,pdf_magic_ok,pdf_openable,page_count,encrypted,quota_reservation_id,quota_terminal_event_id,status,stop_code,created_at_utc,reused_without_new_trigger,external_evidence_hash

  • row_id=sha256(task_id|run_id|item_id|report_key|status);成功必须 34 键全部有值或合法 null,且 remote/local bytes/hash 相等、magic/openable true、encrypted false、page_count 与详情一致。
  • status=SUCCESS|DUPLICATE|FAILED|STOPPED;非 SUCCESS 的未知证据保持 null,禁止填造。
  • external_evidence_hash 是该行对应正式 PDF 的 SHA-256;无正式 PDF 时为 null。

3.8 PackageState V002 与终态(有序字段)

每个 item 的 package row 为:item_id,report_key,state,trigger_attempted,quota_state,staging_path,final_path,manifest_row_id,delivery_present,timing_present,terminal_present,stop_code

TerminalRecord V002 固定 30 键:

schema_version,task_id,handoff_id,run_id,mode,status,exit_code,stop_code,blocker,observed_at_utc,started_at_utc,ended_at_utc,total_elapsed_ms,work_deadline_reached,close_deadline_reached,requested,triggered,succeeded,failed,duplicates,gaps,quota_confirmed,quota_uncertain,quota_active,quota_safe_available,items,manifest_path,delivery_path,timing_path,prohibited_action_attestation

items 按 item_id 排序,元素即上述 package row。任何路径不存在时 null;prohibited_action_attestation 固定对象:remote_original_modified:false,credential_persisted:false,access_control_bypassed:false,body_parsed:false,research_conclusion_generated:false,external_message_sent:false

4. 候选、早停、详情确认与补扫状态机

4.1 两层集合

  • provisional_set:结果页可见字段通过“可先判断”的过滤规则;只用于决定是否暂停扫描、安排详情复核,不等于合格候选。
  • confirmed_set:详情页字段与结果页一致、所有硬过滤通过、去重完成;只有该集合可 reservation/trigger。
  • stable_top_k:连续两屏 provisional top quantity+2 的 id+score 不变,只产生 PAUSE_FOR_DETAIL,不产生最终早停。

4.2 固定流程

  1. 从 checkpoint 或第 1 屏开始,至少扫描 3 屏;每屏落内存 candidate/checkpoint,不写正式结果。
  2. 达到 provisional quantity+2 且两屏稳定时保存 checkpoint,暂停扫描并按 score/既有并列规则逐个 confirm_detail
  3. 详情字段必须与结果页 title/institution/date/pages/analysts 可比字段一致;不一致为 DETAIL_RESULT_MISMATCH 并 REJECTED。
  4. 同机构上限、报告 identity 去重、日期/页数/类型/分析师过滤在详情确认后再次执行。
  5. confirmed_set>=quantity 才允许最终结束扫描;否则调用 restore(checkpoint),从该 cursor 的下一未处理屏继续。
  6. 详情打开失败、详情不一致、duplicate、硬过滤失败、trigger 前页面漂移、trigger 后确认未触发,都不会把 provisional 当成功;恢复后继续扫描。
  7. 扩展扫描终止只允许:confirmed 数量满足;连续 3 屏无新增 provisional 且所有 provisional 已详情判定;或达到 20 屏/100 unique 上限。数量不足时返回 gap,不伪装成功。

4.3 checkpoint 恢复

恢复路径固定为:回到搜索入口→重新输入同 query→按 scroll_count 重放纵向翻页→比较 first/last visible key 与 page_fingerprint→从已记录 seen ids 后继续。每步都受 Budget;任一 anchor/fingerprint 不一致停止,不猜坐标。测试必须覆盖详情失败、详情/结果不一致、重复候选、trigger 未发生、早停后补扫成功、恢复 fingerprint 漂移和硬上限不足。

5. 单一时间预算与子进程/阻塞 I/O 生命周期

5.1 从“请求已观察”开始计时

调用方在观察请求且环境可用时写入 TaskSpec.observed_at_utc;CLI 启动立即读取同主机 UTC,并计算 startup_elapsed_ms=now_utc-observed_at_utc。允许时钟前进,若 observed 在未来超过 2 秒、无法 parse、时差计算异常或 startup elapsed 已达到 600000ms,返回 BLOCKED_INPUT/TIME_BUDGET_STOP,不触发 UI。

CLI 随后记录 start_monotonic_ns,并创建:

  • close_deadline=start_monotonic + max(0,600000-startup_elapsed_ms)
  • work_deadline=close_deadline-10000ms;最后 10 秒只用于 kill/drain、quota 收口、timing/terminal,不得启动新 UI、pull、hash、publish 或 manifest 行。
  • 批量首 item 继承上述 deadline;每个后续 item 在前一 item terminal 时创建 min(close_deadline, item_start+240000ms) 的 item deadline。总体任务不得超过首份 600 秒;如任务明确允许批量持续,batch task spec 必须把 total_budget_ms 写成 600000+240000*(quantity-1),首份仍独立 600 秒。V002 默认按该公式验证,不接受其他值。

所有 SLA 总耗时使用 ended_at_utc-observed_at_utc 与进程 monotonic elapsed 交叉核对;偏差超过 2 秒为 timing invalid,样本计失败。

5.2 ProcessSupervisor 总状态机

固定状态:NOT_STARTED -> STARTED -> EXITED;异常分支 START_FAILED;deadline 分支 TIMEOUT -> TERMINATE_ONCE -> WAIT_BOUNDED -> EXITED_AFTER_TERMINATE|LIVENESS_UNKNOWN

  1. start 前 budget.slice(max_ms, teardown_ms=3000) 必须返回正值;argv 非空字符串数组,shell=False,cwd 固定,stdin/stdout/stderr 为 binary pipes 或 DEVNULL。
  2. Windows 使用一个 per-child Job Object,JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,先 suspended create、成功 assign 后 resume;任一步失败时不运行未受管 child。非 Windows 测试用新 process group 等价模拟。
  3. communicate(timeout=slice);stdout/stderr 各有上限,超限立即进入 terminate,不截断后冒充成功。
  4. timeout/异常只允许一次 TerminateJobObject,随后 wait/communicate 最多 min(3000ms,close_remaining);确认 exit 后才返回。不能确认时关闭 Job handle并返回 LIVENESS_UNKNOWN,CLI 只做内存 terminal,不再读/写可能被 child 改动的 artifact。
  5. ProcessResult 固定字段:argv_redacted,started,pid,exit_code,timed_out,terminate_issued,exited,liveness_unknown,stdout_bytes,stderr_bytes,stdout_truncated,stderr_truncated,started_at_utc,ended_at_utc,elapsed_ms,error_code。凭据永不进入 argv/stdin/output。

5.3 锁与文件 I/O

  • 锁:每 50ms 非阻塞尝试,最长 min(2000ms,work_remaining);失败 LOCK_TIMEOUT,不得阻塞到 deadline。
  • pull 输出仅到 staging;复制/hash 每 1MiB 前后 checkpoint;截图/小 JSON 每 64KiB。到期立即关闭句柄,保留 staging,不登记 success。
  • flush/fsyncpdfinfo 等不可中断边界仅在 work_remaining>=5000ms 时进入;进入前后均 checkpoint。调用返回晚于 work deadline 时结果一律 TIME_BUDGET_STOP,即使 bytes/hash 正确也不得发布/登记 SUCCESS。
  • manifest/delivery/timing/terminal 进入各自步骤前保留 2000/2000/2000/3000ms;不足则跳过下游、在 console terminal 中把对应 present=false。close deadline 到达后不得再做文件写,直接返回内存 terminal。
  • hang/kill/wait、stdout overflow、锁阻塞、copy/hash 分块到期、fsync late、pdfinfo late、manifest late、terminal persist late 都必须有 deterministic fake-clock/subprocess tests。

6. 原子归档、崩溃恢复与 package closure

6.1 staging 与 no-replace 发布

  1. staging 必须在最终目标同一 NTFS volume、同一 destination 目录下的 .hibor-staging/,名称 <run_id>.<item_id>.<nonce>.part,以 os.open(O_CREAT|O_EXCL|O_WRONLY) 创建;staging 永不被当正式 PDF。
  2. pull/copy 完成后 flush+os.fsync,关闭;独占只读 reopen,验证 %PDF-、remote/local bytes/hash、pdfinfo open/page/encryption 和详情页一致。
  3. 发布使用 Win32 CreateHardLinkW(final,staging):同卷、原子创建新目录项、final 已存在则失败且绝不替换;随后只读 reopen final 再校验 bytes/hash,最后 unlink staging。若 hard-link capability/reparse/volume 不符合,预检 STOP,不降级为覆盖 rename/copy。
  4. crash before link:只有 .part;crash after link before unlink:final 与 .part 指向同一已验证文件;恢复以 file-id+bytes/hash 证明同一后清理 staging。不同 hash、unknown file-id 或 final reparse 一律冲突 STOP。
  5. 任何最终路径若已存在:ordinary file 且 hash 等于期望→DUPLICATE,复用原文件但不重复 consume;其他 object/hash→FINAL_PATH_CONFLICT。不删除、截断、覆盖或改名模拟器原件和既有正式文件。

6.2 package state/expected-presence(总序)

state PDF staging final PDF quota terminal manifest delivery timing terminal 允许后继
P00_INIT N N N N N N N preflight
P01_RESERVED N N N N N N N trigger
P02_TRIGGER_UNKNOWN N N U N N N N close only
P03_CACHE_MATCHED N N V N N N N stage
P04_STAGING_PARTIAL I N V N N N N recover/stop
P05_STAGING_VALID V N V N N N N publish
P06_PUBLISHED N V V V N N N N manifest
P07_MANIFESTED N V V V V N N N delivery
P08_DELIVERY N V V V V V N N timing
P09_TIMING N V V V V V V N terminal
P10_CLOSED N V V N V U N V N V N V N V N none

N=absent,V=known valid,I=known partial/invalid,U=unknown。顺序固定:quota trigger 终结→PDF publish→manifest→delivery→timing→terminal。上游失败不补造下游;P10 terminal 根据实际 presence 记录,不要求为了“闭包好看”写假文件。

6.3 恢复

每个 staging 创建后同步写一个同目录 recovery JSON(同样 O_EXCL),字段为 schema_version,run_id,item_id,remote_path,remote_bytes,remote_sha256,staging_path,final_path,stage,created_at_utcresume-postprocess 仅在:无 active UI/process、quota 已是 confirmed/uncertain、remote identity 不变、staging/final 可安全复核时继续;否则 STOP。故障注入点固定为 create 前、每 1MiB 后、fsync 前后、hardlink 前后、final reopen 后、manifest/delivery/timing/terminal 前后,断言正式路径不会指向 partial。

7. 额度并发、reducer 与 replay

7.1 唯一临界区

账本旁固定 daily_quota_<date>.lock。第一次以 O_CREAT 建立普通 lock file,之后所有进程打开同一文件并用 msvcrt.locking(LK_NBLCK,1) 锁第 0 字节;lock file 不删除。每个 quota public API 在同一个锁持有期内严格执行:

read all bytes -> strict CSV/header/date/row validation -> fold -> idempotency lookup -> APP visible remainder conservative reconcile(if provided) -> safe_available check -> construct one row -> append -> flush -> fsync -> reopen/read/fold verify -> unlock

锁前后都 checkpoint;锁 timeout/ledger unreadable/invalid/reopen mismatch 为 STOP,不点击原文。不得缓存旧 fold 后另开写事务。

7.2 reducer

  • baseline_floor=max(all BASELINE_ESTIMATE/CORRECTION_RAISE.baseline_floor);2026-07-29 首次必须 >=3,证据 ref 为中国中免任务。
  • confirmed=baseline_floor + count(unique idempotency keys terminal CONSUME_CONFIRMED)uncertain=count(...CONSUME_UNCERTAIN)active=count(RESERVE without exactly one terminal)
  • safe_available=max(0,27-confirmed-uncertain-active);APP 可见剩余若可信,则 app_consumed=30-visible_remaining,有效 confirmed floor 取本地与 APP 更保守者;APP 不可信不降低本地值。
  • RESERVE 只在 safe_available>=1、同 key 无 terminal/active 时写入。写入并 reopen 验证前不得点击。
  • CONSUME_CONFIRMED/UNCERTAIN/RELEASE 必须引用 active reservation 并使其恰好终结一次;重复相同请求返回原 event,不追加。
  • correction 只提高 baseline 或以明确 ref 把残留 reservation终结;禁止负 confirmed/uncertain、降低 baseline 或删除历史。

7.3 崩溃/竞争测试

必须用两个真实测试进程竞争最后一个 safe slot,唯一一个 RESERVE 成功;覆盖锁 timeout、append 前崩溃(无行)、append 后 fsync 前/后、reopen mismatch、同 CLI 重放、同 handoff 不同 payload 冲突、残留 reservation、release/confirmed/uncertain replay、baseline>=3、correction raise、跨日 ledger、APP 值更保守。所有测试用临时账本,不接 APP、不消耗额度。

8. 错误、终态、exit code 与 precedence

precedence 从高到低:LIVENESS_UNKNOWN/STATE_UNCERTAIN -> ACCESS_CONTROL -> TIME_BUDGET -> QUOTA -> INPUT -> ENVIRONMENT -> AMBIGUOUS_MAPPING -> VALIDATION -> INTERNAL -> PARTIAL/SUCCESS。高优先级证据不得被后续低优先级覆盖。

terminal status stop/error family exit
SUCCESS 全部 requested item P10 success 0
PARTIAL_SUCCESS 至少一个 success,存在非 quota/time 的已知 gap 2
TIME_BUDGET_STOP deadline/late I/O/subprocess timeout 10
PARTIAL_QUOTA_STOP safe_available 不足且有有效子集 11
BLOCKED_INPUT task/schema/replay contract 12
BLOCKED_ACCESS_CONTROL login/CAPTCHA/paywall/subscription/permission 13
BLOCKED_ENVIRONMENT device/package/cache/tool/lock/liveness known failure 14
BLOCKED_AMBIGUOUS_MAPPING cache/candidate/final identity 多义 15
VALIDATION_FAILED PDF/hash/page/manifest/package validation 16
INTERNAL_ERROR 未分类异常,证据保持 null 20
STATE_UNCERTAIN child/file/quota liveness 或事实未知 27

成功子集存在时,TIME/QUOTA/ACCESS/STATE_UNCERTAIN 仍保留该高优先级 terminal,不降为 partial。CLI 捕获裸异常为 INTERNAL_ERROR/20;任何 terminal 中 status/exit_code/stop_code/blocker 必须互相符合本表。

9. Manifest/delivery/timing/terminal 发布规则

  • PDF/manifest/quota 是正式不可变证据;manifest 使用固定 34 列,单锁 read/validate/idempotency→append/fsync/reopen,row_id replay 同整行返回已有;冲突 STOP。
  • delivery.md 只列元数据、文件路径、选择理由、状态、缺口与额度摘要,不含正文/结论;其源数据必须来自已验证 model,不从文件名猜。
  • timing_manifest.csv 固定 12 列,所有已进入阶段均有一行,未进入阶段不造行。
  • terminal JSON 是 package 最后一个可选持久化对象;无剩余 close budget 时只向 stdout 输出严格 JSON,并标 terminal_path=null,terminal_present=false。工具不发送 Codex/网络消息。
  • 所有正式输出记录实际 bytes/hash 到运行 receipt;不以 hash 匹配替代语义验证。

10. 测试与性能总体

10.1 L0/L1/L2(零真实 trigger)

  1. L0:py_compile、import、禁止 API/shell/credential 静态扫描。
  2. L1:标准库 unittest 覆盖全部 schema/交叉约束、candidate/cursor、Budget/ProcessSupervisor、quota reducer/并发、atomic publish/package matrix、manifest/terminal 与错误映射。
  3. L2:fake ADB + 合成 PDF + 临时 NTFS 目录,真实启动可控 helper child 测 hang/kill/wait/stdout overflow;dry-run 必须 real_adb_action_count=0,trigger_count=0,real_quota_write_count=0
  4. 实现复审前必须提供 test case→requirement→result trace matrix;失败可在 L0/L1 范围修复重跑,保留失败记录,不需逐次管理 A001。

10.2 真实性能计划(最多 10 个 distinct trigger)

在第一次真实 reserve 前生成并锁定 performance_plan.json:10 个 primary item + 最多 3 个 no-trigger alternate;字段固定 plan_id,created_at_utc,slot_id,mode,batch_id,item_order,query,expected_report_identity,external_exclusion_allowed,selected_from_readonly_discovery。该计划的实际 bytes/hash 写入执行日志,之后不替换已触发 slot。

固定总体:

slots mode 计入单份样本 trigger 上限 批量角色
PERF-S01..PERF-S06 6 次 collect-one 6 6
PERF-B01-I01..I04 1 次同 query collect-batch 的 4 个 item 4 4 I01=首份,I02-I04=增量
PERF-A01..A03 预登记 alternate 仅替代 trigger 前 external-excluded primary 合计仍不得超过 10 保持被替代 slot mode

因此批量每个 item 同时是一个真实单份样本,正式 PASS 分母固定为 10,distinct trigger 最大也是 10;不得为了凑 PASS 触发第 11 份。alternate 仅在 reserve/点击前出现有证据的 external blocker 时替换,且被替代 primary 永久标 EXCLUDED_PRETRIGGER_EXTERNAL

10.3 纳入、排除与统计

  • slot 在 preflight 后第一次成功 RESERVE 时进入 SLA population;从 task observed 到该 item terminal 是单份总耗时。任何 TIME_BUDGET_STOP、internal、validation、mapping、quota-state failure 都保留在分母并令 performance acceptance 失败,不能删样本。
  • 可排除项只有在 RESERVE/点击前确认的:登录/验证码/付费/权限、模拟器离线、APP 服务不可用或报告已不可访问;必须有截图/UI dump/process receipt,且使用预登记 alternate。触发后才出现的外部问题仍计 SLA failure。
  • collect-batch I01 从 batch request observed 到 I01 terminal;I02-I04 增量从前一 item terminal monotonic 到本 item terminal。每个 item 也记录自身 start-to-terminal。
  • median:10 个总耗时排序后第 5/6 平均;nearest-rank P90:排序第 ceil(0.9*10)=9 个。任何计入样本非成功、任何验证项失败或样本少于 10,整体只能 PERFORMANCE_FAILEDINSUFFICIENT_PERFORMANCE_SAMPLES
  • PASS 必须同时:10/10 item success;每份 magic、remote/local bytes/hash、openability、page_count、manifest、quota 全通过;median<=480000ms;P90<=600000ms;B01-I01<=600000ms;I02-I04 每份 incremental<=240000ms;trigger_total<=10;confirmed+uncertain+active 永不超过 27。
  • 当日安全额度不足以做完时保留已完成样本并 INSUFFICIENT_PERFORMANCE_SAMPLES 顺延到次日新 ledger;不同日期样本可合并,但每个 slot 仍唯一、每日日限独立,统计不可重置失败 slot。

11. 实施顺序与验收矩阵

设计 PASS 后按以下顺序实施:

  1. models/budget/process + schema/child tests;
  2. quota 原子 reducer + 双进程测试;
  3. candidate/scan checkpoint + fake UI 测试;
  4. archive staging/publish/recovery + crash injection;
  5. manifest/delivery/timing/terminal + package matrix;
  6. CLI 集成与 fake ADB dry-run;
  7. 开发自检与独立实现复审前置检查;
  8. 真实 10-trigger 性能计划、执行、证据与最终独立复审。
blocker V002 closure 强制测试
01 第 2–3、8 节 API/schema/call graph/exit schema/order/null/extra-key/typed error
02 第 5 节 observed→monotonic、Job child、bounded I/O hang/kill/lock/late copy/hash/fsync/terminal
03 第 4 节 provisional/confirmed/checkpoint mismatch/duplicate/detail fail/补扫/漂移
04 第 6、9 节 staging+hardlink+closure 每边界 crash、冲突、duplicate、恢复
05 第 3.5、7 节 atomic quota/replay 双进程、崩溃、重复、残留、跨日
06 第 10.2–10.3 节固定 6+4 population external exclusion、failure inclusion、median/P90

实现不得以跳过 hash、页数、manifest、quota、access-control 或 terminal closure 换取 SLA。开发员只做实现与自检,最终由 dev.reviewer.ana.cai 独立审核。

12. 当前状态

PENDING_INDEPENDENT_DESIGN_REREVIEW

V002 复审 PASS 前,候选源码/测试/额度账本仍不存在;不得运行 Python、ADB、慧博 APP、真实或合成采集。当前真实 trigger 增量=0,2026-07-29 已知外部基线仍按至少 3 保守处理。