# 慧博 10 分钟快速采集工具开发方案 V005 创建人员:`dev.developer.ana.cai` 文件职责:append-only 关闭 V004 唯一 blocker,冻结 `TerminalItem` 新触发/纯复用/失败/STOP 的 quota 真值表。 predecessor:V004=`11939/08e12872b9e00322fbeb669c2c2d97a48fa3724f3ae6e759e9b4c2e2b957df08`;review=`AUDIT-DEV-ANA-HIBOR-FAST-COLLECTION-DESIGN-V004=HOLD/1/1`。 继承:BLOCK-02/03/04/05/06 均已关闭或无回退;V004 的 terminal mapping、manifest evidence rule、五类 quota idempotency 与单行 reconcile→reserve 事务全部不变。V005 仅替换 V004 第 1.2 节中 SUCCESS/DUPLICATE/FAILED/STOPPED 的 quota ID 强制条件。 ## 1. `TerminalItem V005` 字段不变 仍为固定 26 键: `item_id,slot_id,candidate_id,report_identity,state,status,stop_code,trigger_attempted,triggered,quota_state,quota_reservation_id,quota_terminal_event_id,quota_artifact_event_id,title,institution,report_date,analysts,page_count,bytes,sha256,source_cache_path,source_file_name,final_path,manifest_row_id,reused_without_new_trigger,error_or_note` 新增约束:quota 三个 ID 的 nullability 只由下表 variant 决定,不再由 `status=SUCCESS` 无条件决定。`quota_state=NONE` 时三个 ID 必须全 null,且该 item 不调用任何 QuotaLedger 写 API;`quota_state=RESERVED|CONFIRMED|UNCERTAIN|RELEASED` 时按表要求。 ## 2. 完整真值表 `NNN` 表示 reservation/terminal/artifact 三个 quota ID 全 null;`RRR` 表示 reservation、quota terminal、artifact terminal 全非空;`RRN` 表示 reservation 和 quota terminal 非空、artifact null;`RNN` 表示仅 reservation 非空。 | variant | state | status | trigger_attempted | triggered | reused | quota_state | quota IDs | final PDF | manifest | stop/error | |---|---|---|---:|---:|---:|---|---|---|---|---| | `NEW_TRIGGER_SUCCESS_CONFIRMED` | P10_CLOSED | SUCCESS | true | true | false | CONFIRMED | RRR | V | V | null/NONE | | `NEW_TRIGGER_SUCCESS_UNCERTAIN` | P10_CLOSED | SUCCESS | true | null | false | UNCERTAIN | RRR | V | V | TRIGGER_UNCERTAIN + 已确认 cache identity | | `PROJECT_REUSE_SUCCESS` | P10_CLOSED | SUCCESS | false | false | true | NONE | NNN | V existing | V | null/NONE | | `CACHE_REUSE_SUCCESS` | P10_CLOSED | SUCCESS | false | false | true | NONE | NNN | V newly published | V | null/NONE | | `DUPLICATE_NO_TRIGGER` | P10_CLOSED | DUPLICATE | false | false | true | NONE | NNN | V existing | V | DUPLICATE_EXISTING_ARTIFACT | | `DUPLICATE_AFTER_CONFIRMED` | P10_CLOSED | DUPLICATE | true | true | false | CONFIRMED | RRR | V existing | V | DUPLICATE_EXISTING_ARTIFACT | | `DUPLICATE_AFTER_UNCERTAIN` | P10_CLOSED | DUPLICATE | true | null | false | UNCERTAIN | RRR | V existing | V | DUPLICATE_EXISTING_ARTIFACT | | `RELEASED_NO_TRIGGER_CLOSED` | P10_CLOSED | STOPPED | true | false | false | RELEASED | RRN | N | V | TRIGGER_NOT_OCCURRED | | `UNCERTAIN_NO_ARTIFACT_CLOSED` | P10_CLOSED | STOPPED | true | null | false | UNCERTAIN | RRN | N | V | TRIGGER_UNCERTAIN | | `PROCESSING_FAILED_CONFIRMED_CLOSED` | P10_CLOSED | FAILED | true | true | false | CONFIRMED | RRR | N | V | actual processing error | | `PROCESSING_FAILED_UNCERTAIN_CLOSED` | P10_CLOSED | FAILED | true | null | false | UNCERTAIN | RRR | N | V | actual processing error | | `RESERVED_BEFORE_ACTION_CRASH` | P01_RESERVED | STOPPED | false | false | false | RESERVED | RNN | N | N | STATE_UNCERTAIN | | `RESERVED_DURING_ACTION_CRASH` | P02_TRIGGER_UNKNOWN | STOPPED | true | null | false | RESERVED | RNN | N | N | STATE_UNCERTAIN | | `POST_QUOTA_PRE_ARTIFACT_STOP` | P02_TRIGGER_UNKNOWN | STOPPED | true | true | false | CONFIRMED | RRN | N | N | high-precedence close STOP | | `POST_PUBLISH_PRE_MANIFEST_STOP` | P06_PUBLISHED | STOPPED | true | true | false | CONFIRMED | RRR | V | N | PERSIST_LATE | | `PRE_RESERVATION_STOP` | P00_INIT | STOPPED | false | false | false | NONE | NNN | N | N | actual input/environment/access/quota/time STOP | 交叉规则: 1. `PROJECT_REUSE_SUCCESS` 只复用项目内已归档 ordinary non-reparse PDF,必须重验 bytes/hash/%PDF-/openability/page count 与既有 manifest 关系;不读 APP、不读 cache、不 reserve、不写任何 quota event。 2. `CACHE_REUSE_SUCCESS` 只允许 `resume-postprocess` 使用任务已记录的 cache identity、remote bytes/hash 与 `reused_without_new_trigger=true`,不得打开 APP 原文;可只读 ADB pull 已存在缓存,但不调用 observe/reserve/terminal/artifact quota API。 3. 两类纯复用即使 `QuotaSnapshot.safe_available=0` 仍允许;CLI 只读 fold 当日 ledger 并把 `quota_ledger_path`、累计量和 safe=0 写入 top-level terminal,不新增行。ledger lock/read失败仍 fail-closed,但“额度为零”本身不是复用 blocker。 4. 纯复用的 manifest `quota_reservation_id,quota_terminal_event_id,quota_artifact_event_id` 三列为空;`reused_without_new_trigger=true`,`error_or_note` 写 `ZERO_QUOTA_REUSE`,其余 PDF/来源证据必须完整。 5. `record_artifact_outcome` 仅用于存在 reservation 的 item;因此两个 NEW_TRIGGER、两个 DUPLICATE_AFTER 和两个 PROCESSING_FAILED closed variant 为 RRR。纯复用的成功/重复计数来自 manifest/terminal,不写 quota ledger success delta。 6. release/uncertain durable 且 artifact 未形成时为 RRN;quota terminal 后 close deadline 到期也为 RRN。PDF 已 publish 后 artifact success 必须先 durable,再进入 manifest,因此 `POST_PUBLISH_PRE_MANIFEST_STOP=RRR`。 7. `trigger_attempted` 表示已进入打开原文动作;V005 把 `triggered` 的类型精确扩为 `BOOL|null`:确认触发=true、确认未触发=false、触发事实不确定=null;其余 BOOL 字段不可 null。 ## 3. Package/manifest/terminal 一致性 - pure reuse item 可从 V002 package P00 直接进入既有证据验证,再到 P06/P07/P08/P09/P10;不得伪造 P01 reservation。package row 的 `trigger_attempted=false,quota_state=NONE,reused=true`。 - top-level `triggered` 不含纯复用;`succeeded` 包含复用;`quota_confirmed/uncertain/active` 来自只读 ledger snapshot;三者不会因复用增加。 - 若同一任务同时有纯复用和新触发,先完成所有可证明复用 item,再对剩余 item 逐个 reserve;safe=0 时保留复用 success,剩余 item 返回 PARTIAL_QUOTA_STOP。 - public terminal 的 item tuple 逐项满足本表;manifest 48 列与 terminal item 的 status/path/hash/quota IDs/reuse BOOL 必须相等。 ## 4. 强制测试与当前 gate 1. 上表 16 个 variant 各一正向 literal,逐字段验证 26 键、BOOL/null、quota ID pattern、state/path/manifest presence。 2. 负向:pure reuse 带任一 quota ID、reused=false、triggered=true;new trigger success 缺任一 RRR;release/uncertain 无 terminal id;artifact absent 却声称 RRR;均 ContractError。 3. `safe_available=0` 下 PROJECT_REUSE_SUCCESS、CACHE_REUSE_SUCCESS 均 PASS,quota ledger bytes/hash/row count 前后不变;随后新 trigger 必须 PARTIAL_QUOTA_STOP。 4. 混合 batch:2 个 reuse + safe=1 的 1 个新 trigger + 1 个 quota stop,trigger count=1、success=3、quota 增量=1,manifest/terminal 一致。 5. cache identity/hash 证据不足时不得走 CACHE_REUSE;返回 BLOCKED_AMBIGUOUS_MAPPING,不得借“复用”规避 quota。 当前状态:`PENDING_INDEPENDENT_DESIGN_REREVIEW`。V005 PASS 前仍不创建源码/测试/额度账本,不运行 Python/ADB/APP/dry-run,不产生真实 trigger;新增 trigger=`0`。