From b7ddab8f0279f974da3147aebcb9b0d1f8257994 Mon Sep 17 00:00:00 2001 From: Zhixin Eason Li Date: Wed, 5 Aug 2026 16:20:03 -0400 Subject: [PATCH 1/3] task_20260805: audit ADC_surfaceome_reference@0.3.0 for SRCADM-01 Carries out the nine-item audit PR #59 froze and approved, which is the authority for this work. The builder source, the raw manifest, the checksum file, the licence declarations and the three processed tables were read rather than described, and every verdict records a recheckable basis. Conclusion: admissible with conditions. No item failed. Four items pass with a finding, each with its impact bounded. AUD-05 is the item the reviewer singled out, and it passes for a specific reason. The three families map to curated_knowledge from goa_human and uniprot_reviewed_human, imaging from hpa_subcellular_location, and cell_surface_capture_ms from cspa. GOA human is produced by the UniProt curation pipeline, so those two are the same origin - and the builder collapses them into one family rather than counting two. GUCY2C is the worked example: both sources support, family_count stays 1. That is exactly the double-counting failure mode the reviewer named, and the builder avoids it. Two reinforcing checks: family counting keys on supporting evidence, verified by counterexample over the 11,334 genes with an HPA row but hpa_plasma_membrane false, of which zero had imaging counted; and because curated_knowledge is the only family two sources can feed while counting once, family_count of two or more always includes an experimental family, so RQ-01's bar is not a formality. AUD-04 finds six sources with ambiguous licences - four cellphonedb files, cellchatdb under a GPL-3.0 repository licence, and omnipath retaining per-resource terms - and then bounds it: none of the six appears in source_evidence.tsv at all. They feed only the cci_receptor fields, which PR #59 already bars. The three sources that do reach it are CC BY 4.0. That conclusion is load-bearing on the field whitelist, so widening the whitelist voids the admission, recorded as COND-02. AUD-09 verifies all 19 raw files against checksums.sha256 and finds the builder deterministic apart from a timestamp field, but uniprot_reviewed_human and goa_human carry current_at_download rather than a release, so re-downloading cannot reproduce the snapshot byte for byte. Admission therefore rests on the archived snapshot, recorded as COND-03. AUD-06 finds no explicit dedup routine and 6 plus 5 duplicate keys, none of which touches the 41 targets, and family counts derive from consensus booleans rather than row counts, so duplicates cannot inflate RQ-01. AUD-01 records honestly that builder_version comes from config rather than a constant, so the 0.3.0 label cannot be confirmed by the script itself. This PR grants nothing. Status stays pending_review, admission_record_ref stays null in both files, and EVGAP-01's extraction stays unauthorised. 351 tests pass. Eleven mutations caught and rolled back exactly. Two of them escaped the first pass because my assertions matched substrings that survived a partial deletion; the assertions were tightened to require the substantive content and the escapees were re-tested and caught. Co-Authored-By: Claude Opus 5 --- ...05-srcadm-01-surfaceome-admission.zh-CN.md | 80 +++++++ .../pools/srcadm_01_surfaceome_admission.yaml | 192 +++++++++++++++++ ...ADM_01_SURFACEOME_ADMISSION_AUDIT.zh-CN.md | 115 ++++++++++ logs/worklog.md | 21 ++ tests/test_srcadm_01_surfaceome_admission.py | 199 ++++++++++++++++++ 5 files changed, 607 insertions(+) create mode 100644 docs/handoff/2026-08-05-srcadm-01-surfaceome-admission.zh-CN.md create mode 100644 docs/pools/srcadm_01_surfaceome_admission.yaml create mode 100644 docs/tasks/SRCADM_01_SURFACEOME_ADMISSION_AUDIT.zh-CN.md create mode 100644 tests/test_srcadm_01_surfaceome_admission.py diff --git a/docs/handoff/2026-08-05-srcadm-01-surfaceome-admission.zh-CN.md b/docs/handoff/2026-08-05-srcadm-01-surfaceome-admission.zh-CN.md new file mode 100644 index 0000000..6105894 --- /dev/null +++ b/docs/handoff/2026-08-05-srcadm-01-surfaceome-admission.zh-CN.md @@ -0,0 +1,80 @@ +# Handoff:SRCADM-01 surfaceome 来源准入审计 + +- 日期:`2026-08-05` +- 任务分支:`task_20260805_srcadm-01-surfaceome-admission` +- 基线:`main` @ `8aa7e87` +- 授权依据:审计范围 `AUD-01`..`AUD-09` 由 **PR #59** 冻结并获 `APPROVE`,即本审计的授权 +- 交付物类型:**审计记录(contract-only,无外部运行产物)** +- 结论:**`admissible_with_conditions`**——有条件可纳入,四项条件 +- 架构变更:`NO_ARCHITECTURE_CHANGE` +- 审核状态:等待 ChatGPT `APPROVE`。**本 PR 不适用 `AGENTS.md`「审核豁免」。** + +## 一、本次范围 + +人类负责人指示起 `SRCADM-01`。这是 Track B 的第一环,也是唯一挡住 `EVGAP-01` 的一环。 + +PR #59 明确「派生数据库不能靠自声明 + 哈希纳入」,并冻结九项必审内容。本次是那九项的实际审计——**读了 builder 源码、raw manifest、license 声明与 processed 表,而不是描述它们**。 + +**本 PR 不授予准入。** 准入由获 `APPROVE` 后成立,届时另开 PR 把 `EVGAP-01` 的 `admission_record_ref` 指向审核记录。 + +## 二、仓库内交付了什么 + +| 文件 | 作用 | +|---|---| +| `docs/tasks/SRCADM_01_SURFACEOME_ADMISSION_AUDIT.zh-CN.md` | 审计文档(面向操作者,中文) | +| `docs/pools/srcadm_01_surfaceome_admission.yaml` | 机器可读审计结论:九项 verdict 与依据、四项准入条件 | +| `tests/test_srcadm_01_surfaceome_admission.py` | 13 项校验 | + +## 三、九项结论 + +`AUD-01` PASS|`AUD-02` PASS(实算复核)|`AUD-03` PASS_WITH_FINDING|`AUD-04` PASS_WITH_FINDING|`AUD-05` PASS|`AUD-06` PASS_WITH_FINDING|`AUD-07` PASS|`AUD-08` PASS|`AUD-09` PASS_WITH_FINDING + +**无一项 FAIL**,四项带 finding 且全部界定了影响范围。 + +## 四、三个最关键的结论 + +**`AUD-05` 独立性成立,而且恰好避开了审核方点名的失效模式。** 三家族映射为 `curated_knowledge <- {goa_human, uniprot_reviewed_human}`、`imaging <- {hpa_subcellular_location}`、`cell_surface_capture_ms <- {cspa}`。**`goa_human` 与 `uniprot_reviewed_human` 同源**(GOA human 由 UniProt 策展流程产出),而 builder 把二者收进同一个家族,因此不会重复计数——`GUCY2C` 两来源皆 supported,`family_count` 仍为 1。两条加强验证:family 计数要求**支持性**证据(反例检验 11,334 个 HPA 有行但 `hpa_plasma_membrane=false` 的基因中,`imaging` 被计入的为 **0**);`family_count >= 2` 必然包含至少一个实验型家族。 + +**`AUD-04` license 歧义存在但不触及 EVGAP-01 读取的字段。** 六个来源 license 有歧义(cellphonedb ×4、cellchatdb GPL-3.0、omnipath per-resource),**但没有任何一个出现在 `source_evidence.tsv` 中**——它们只喂已被 PR #59 禁用的 `cci_receptor_*`。进入的四个来源里三个是 CC BY 4.0。**该结论承重**:依赖 #59 的字段白名单,白名单一扩大准入即失效(`COND-02`)。 + +**`AUD-09` 完整性可验、可复现性有边界。** 19 个 raw 文件 `shasum -c` 全部 OK;builder 唯一时间依赖是时间戳字段、无 random。**但 `uniprot_reviewed_human` 与 `goa_human` 的 release 是 `current_at_download`,不是版本号**,从上游重新下载不保证逐字节复现。可复现性成立的前提是使用已归档 snapshot(`COND-03`)。 + +## 五、四项准入条件 + +`COND-01` 仅限这一个 snapshot(23 个校验和钉住)|`COND-02` 仅限 #59 字段白名单|`COND-03` 基于已归档 snapshot 而非可从上游复现|`COND-04` 重复键不得进入 EVGAP-01 判据(靶点轴扩大须重查) + +## 六、其余两项 finding + +**`AUD-03`**:manifest 的 `release` 字段全部为 null,release 字符串由 builder 另行赋值;四个在用来源中两个是 `current_at_download`。 + +**`AUD-06`**:builder 中检索不到显式去重例程;实测 6 + 5 个重复键。受影响的 11 个基因**没有一个属于 41 个靶点**,且 family 计数取自 support 布尔值而非行数,故重复行结构上不可能抬高 `RQ-01`;41 个靶点的 `family_count` 与 `families` 列表实测 100% 一致。 + +## 七、一处如实记录的残留 + +`builder_version` 由 config 传入而非脚本内常量,故「0.3.0」这个版本号依赖 `build_manifest` 自述、**不能由脚本自身独立确证**。不构成阻断(snapshot 由校验和钉住,版本号只是标签),但记录在案。 + +## 八、明确没有做什么 + +- **没有授予准入**;没有修改 `evgap_01_surface_localization_extraction.yaml` 的 `admission_record_ref`(仍为 `null`,`authorises_extraction_run` 仍为 `false`)。 +- 没有执行 `EVGAP-01` 抽取;没有执行 Level 01;没有解除 `EVGAP-01` 或 `EVGAP-02`。 +- 没有纳入该数据集的其他版本或后续重建;没有扩大 PR #59 的字段白名单。 +- 没有纳入 `SRCADM-02`..`SRCADM-05` 的任何派生库。 +- 没有产生任何外部运行产物——本次是对既有文件的审计,未生成数据。 +- 未补八份批准记录(现为九份,含 #62),事实已查全但未写文件。 + +## 九、验证结果 + +- `Ran 351 tests` 全部通过(`main` 基线 338 + 本次新增 13)。 +- `scripts/verify_repository_boundary.sh` 通过;`git diff --check` 通过;零 `__pycache__`。 +- 测试逐条把审计结论钉在 PR #59 冻结的范围上:九项 ID 集合必须与 `required_audit_items` 完全相等;无 `FAIL`;每项必须有可复核依据(`PASS_WITH_FINDING` 必须写出 finding);`AUD-02` 的摘要必须是 64 位十六进制且与 #59 引用的前缀一致;`AUD-05` 必须点名同源来源对并引用实测反例数;`AUD-04` 依赖的 `cci_receptor_role` 必须确实在 #59 的 `barred_fields` 中;`AUD-06` 的影响界定必须针对 41 靶点轴;本审计不得授予准入。 +- 一处测试自身的错误已修:初稿从 `source_admission_dependency` 读 `raw_manifest_sha256` 键,而该键在 #59 修订后并不存在(摘要写在 `AUD-02` 的条目文本里)。改为跨文件比对前缀并校验 64 位十六进制。 +- **变异检验 11 个,全部被捕获后精确回滚**:自行授予准入、自填 `admission_record_ref`、删掉一项审计、`AUD-05` 去掉同源来源说明、`AUD-05` 声明「有行即计」、结论改为无条件可纳入、删掉白名单条件 `COND-02`、抹去 `AUD-09` 的可复现性限制、把 `AUD-06` 影响界定改成含糊表述、删掉受影响基因清单、把某项判为 `FAIL` 却不改总结论。 +- **其中两个变异首轮逃逸,是我断言太松导致的**:`assertIn("current_at_download", finding)` 与 `assertIn("41", bound)` 在部分删除后仍能命中残留文本。已改为要求 `AUD-09` 的 finding 同时点名两个未钉版本的来源并写出「逐字节」不可复现,要求 `AUD-06` 的 bounded_impact 明确写出「没有一个属于」并列出受影响基因。重测三个变异全部被捕获。 + +## 十、后续顺序 + +1. 本审计 `APPROVE`,在 `logs/` 留审核记录。 +2. **另开 PR** 把 `admission_record_ref` 指向该记录,`authorises_extraction_run` 转 `true`。 +3. 执行 `EVGAP-01` 抽取 → 结果 PR → binding,解除 `EVGAP-01`。 +4. `EVGAP-02` 抽取已执行,结果审核在 PR #62;获批后另开 PR 解除 `EVGAP-02`。 +5. **两个缺口都解除后**才能生成 `ADC_POOL_LEVEL_01_ACCEPTED`。 diff --git a/docs/pools/srcadm_01_surfaceome_admission.yaml b/docs/pools/srcadm_01_surfaceome_admission.yaml new file mode 100644 index 0000000..7654c29 --- /dev/null +++ b/docs/pools/srcadm_01_surfaceome_admission.yaml @@ -0,0 +1,192 @@ +# SRCADM-01:ADC_surfaceome_reference@0.3.0 来源准入审计(machine-readable) +# +# 审计范围 AUD-01..AUD-09 由 PR #59 冻结并获 ChatGPT APPROVE,即本审计的授权依据。 +# 本文件记录逐项审计结论与其可复核依据。它不含任何证据数据。 +# +# 由 tests/test_srcadm_01_surfaceome_admission.py 校验。 +# 本文件**不**授予准入:准入由本 PR 获 APPROVE 后成立,届时另开 PR 把 +# evgap_01_surface_localization_extraction.yaml 的 admission_record_ref 指向审核记录。 + +admission: + admission_id: SRCADM-01 + dataset_id: ADC_surfaceome_reference + dataset_version: 0.3.0 + snapshot_id: 2026-07-29-quant-topology-mm + audit_scope_frozen_by_pr: 59 + audit_scope_items: 9 + status: pending_review + # 结论是「有条件可纳入」,不是无条件可纳入。四项条件见 admission_conditions。 + recommendation: admissible_with_conditions + grants_admission_by_itself: false + admission_record_ref: null + next_step: 获 APPROVE 后另开 PR 把 EVGAP-01 契约的 admission_record_ref 指向审核记录 + +# 逐项审计结论。每一条都必须有可复核的依据,不接受「已阅读」这类断言。 +audit_findings: + - id: AUD-01 + item: builder 实现与版本 + verdict: PASS + evidence: >- + AssetGenOS/scripts/build_t7_surfaceome_reference.py 存在,2,721 行,可读。 + family 计数、discordance 生成、consensus 打分均为显式条件分支,无隐藏启发式。 + residual_note: >- + builder_version 由 config 传入而非脚本内常量,故「0.3.0」这个版本号依赖 + build_manifest 的自述,不能由脚本自身独立确证。 + + - id: AUD-02 + item: raw manifest 与 raw_manifest_sha256 的对应关系 + verdict: PASS + evidence: >- + 对 raw/2026-07-29-quant-topology-mm/download_manifest.json 实算 SHA-256, + 得 884f419118302ae39c3e50292d03295ff676434868e1061b39ead50f9cc977bb, + 与 build_manifest.json 声明的 raw_manifest_sha256 逐字符一致。 + verification_method: recomputed + + - id: AUD-03 + item: 原始来源清单与各自 release 版本 + verdict: PASS_WITH_FINDING + evidence: >- + manifest 列出 19 个文件、19 个不同 source_id。processed 表的 source_release + 有实义取值:cspa「PLOS ONE 2015 supplementary file S2」、 + hpa_subcellular_location「HPA 25.1; Ensembl 109」、 + goa_human「current_at_download; ontology=releases/2026-06-15」、 + uniprot_reviewed_human「current_at_download」。 + finding: >- + manifest 的 files 条目里 release 字段全部为 null;release 字符串由 builder + 另行赋值。且 EVGAP-01 实际使用的四个来源中,**uniprot_reviewed_human 与 + goa_human 的 release 是 current_at_download,不是版本号**——它们只钉住下载 + 时刻,不钉住上游版本。 + consequence: 见 AUD-09 的可复现性边界 + + - id: AUD-04 + item: 每个原始来源的 license 与再分发条件 + verdict: PASS_WITH_FINDING + evidence: >- + 19 个来源全部声明 license,无缺失。进入 source_evidence.tsv 的四个来源中 + goa_human、hpa_subcellular_location、uniprot_reviewed_human 为 CC BY 4.0。 + finding: >- + 六个来源的 license 有歧义:cellphonedb_gene/protein/complex/interaction + 「not declared in cellphonedb…」、cellchatdb_human「GPL-3.0 repository + license」、omnipath_intercell_receptor「per-resource licenses retained」。 + 另有一处命名不一致:processed 表用 source_id「cspa」,manifest 用 + 「cspa_validated_surfaceome」与「cspa_cell_type_matrix」,无法直接 join。 + decisive_check: >- + 实测:这六个 license 歧义来源**没有任何一个出现在 source_evidence.tsv 中**。 + 它们只喂 cci_receptor_* 字段,而该字段已被 PR #59 的 barred_fields 禁用。 + 因此再分发歧义不触及 EVGAP-01 读取的任何字段。 + load_bearing_boundary: >- + 该结论依赖 PR #59 的字段白名单。若白名单日后扩大到 cci_receptor_*, + 本准入必须重新审。 + + - id: AUD-05 + item: independent_evidence_family 的独立性是否真实成立 + verdict: PASS + evidence: >- + 三个家族的来源映射为 curated_knowledge <- {goa_human, + uniprot_reviewed_human};imaging <- {hpa_subcellular_location}; + cell_surface_capture_ms <- {cspa}。 + same_origin_pair_handled: >- + goa_human 与 uniprot_reviewed_human 同源(GOA human 由 UniProt 策展流程产出)。 + builder 把二者收进**同一个** curated_knowledge 家族,因此不会被重复计成两个 + 独立家族——这正是审核方点名的「同源数据重复计数」失效模式,builder 避开了它。 + 实例:GUCY2C 两个来源都 supported,family_count 仍为 1。 + counting_requires_support: true + counting_requires_support_evidence: >- + family 由 consensus 的 support 布尔值推出(builder 第 2112–2118 行), + 不是「有行即计」。实测反例检验:HPA 有行但 hpa_plasma_membrane=false 的基因 + 共 11,334 个,其中 imaging 被计入家族的为 **0 个**。 + structural_guarantee: >- + 由于 curated_knowledge 是唯一可由两个来源喂养但只计一次的家族, + family_count >= 2 必然包含至少一个实验型家族(imaging 或 + cell_surface_capture_ms)。RQ-01 的两家族门槛因此不是形式门槛。 + + - id: AUD-06 + item: 来源去重逻辑 + verdict: PASS_WITH_FINDING + finding: >- + builder 中检索不到显式去重例程。实测 processed 表存在重复键: + source_evidence.tsv 在 (gene_symbol, source_id, evidence_kind) 上有 6 个重复键, + membrane_topology_evidence.tsv 在 (gene_symbol, source_id) 上有 5 个。 + surfaceome_consensus.tsv 在 gene_symbol 上无重复。 + bounded_impact: >- + 重复键涉及的基因为 HERC3、MATR3、NPIPA9、PINX1、POLR2J3、PRODH、 + ERVK-7、NRXN1、NRXN2、NRXN3、SIRPB1,**没有一个属于 EVGAP-01 的 41 个靶点**。 + 且 family 计数取自 consensus 的 support 布尔值而非 source_evidence 行数, + 故重复行在结构上不可能抬高 RQ-01 的家族数;41 个靶点的 family_count 与 + families 列表实测 100% 一致。 + + - id: AUD-07 + item: discordance_flags 的生成规则与覆盖范围 + verdict: PASS + evidence: builder 第 2121–2129 行,四条确定性规则,条件与标记一一对应。 + rules: + - hpa_plasma_membrane_uncertain:HPA reliability 为 uncertain + - cspa_support_without_uniprot_explicit_surface:MS 支持但 UniProt 未显式标注表面 + - hpa_only_vs_curated_knowledge:仅影像支持、策展来源不支持 + - generic_membrane_without_surface_localization:仅泛膜注释、无任何表面定位支持 + relevance_to_evgap_01: >- + PR #59 的 E1-04 把 discordance_flags 非空一律判 DEFER,故这四条规则的作用 + 只是把冲突显式化并触发 DEFER,不会产生 RETAIN。 + + - id: AUD-08 + item: 代表性靶点的逐行来源回溯 + verdict: PASS + evidence: >- + 对三个代表性靶点逐行回溯成功,每条主张都能落到 source_id + surface_supported + + source_release + source_url:CDH17(ECD-a 路径,families + cell_surface_capture_ms+curated_knowledge,n=2)、CEACAM5(ECD-b GPI 路径, + families curated_knowledge+imaging,n=2)、GUCY2C(families 仅 + curated_knowledge,n=1,故在 PR #59 规则下落 E1-02 hold)。 + targets_traced: + - CDH17 + - CEACAM5 + - GUCY2C + + - id: AUD-09 + item: snapshot 是否可由 builder 与 raw manifest 复现 + verdict: PASS_WITH_FINDING + integrity_verified: >- + `shasum -a 256 -c checksums.sha256` 对 raw snapshot 的 19 个文件全部 OK。 + determinism: >- + builder 中唯一的时间依赖是第 268 行 datetime.now() 用于 processed_at_utc + 时间戳字段,不参与任何计算;无 random、无 shuffle。给定同一 raw snapshot, + 构建结果确定。 + finding: >- + **但 raw snapshot 本身对 uniprot_reviewed_human 与 goa_human 未钉版本** + (release 为 current_at_download)。因此从上游重新下载并重建**不能**保证 + 逐字节复现该 snapshot。可复现性成立的前提是使用已归档的 raw snapshot, + 而不是从来源重新获取。 + +# 有条件准入的四项条件。任一条被破坏即须重新审。 +admission_conditions: + - id: COND-01 + condition: 准入仅限这一个 snapshot + detail: >- + dataset_version 0.3.0 与 snapshot_id 2026-07-29-quant-topology-mm, + 由 19 个 raw 文件校验和与 4 个 processed 文件校验和共同钉住。 + 不覆盖该数据集的其他版本,也不覆盖任何后续重建。 + - id: COND-02 + condition: 准入仅限 PR #59 的字段白名单 + detail: >- + license 歧义来源只喂已被禁用的 cci_receptor_* 字段,这是 AUD-04 结论成立的 + 前提。若白名单扩大到这些字段,本准入立即失效、须重新审。 + - id: COND-03 + condition: 准入基于已归档 snapshot,不基于可从上游复现 + detail: >- + 两个来源为 current_at_download,重新下载不保证逐字节一致。归档副本即事实来源。 + - id: COND-04 + condition: 重复键不得进入 EVGAP-01 的判据 + detail: >- + 已实测 11 个受影响基因全部在 41 个靶点之外,且 family 计数不按行数累加。 + 若 EVGAP-01 的靶点轴日后扩大,须重新检查重复键是否触及新靶点。 + +# 本审计明确不做什么。 +not_authorised: + - 授予准入(准入由本 PR 获 APPROVE 后成立) + - 修改 evgap_01_surface_localization_extraction.yaml 的 admission_record_ref + - 执行 EVGAP-01 抽取 + - 执行 Level 01 + - 解除 EVGAP-01 或 EVGAP-02 + - 把该数据集的其他版本或后续重建纳入 + - 扩大 PR #59 的字段白名单 + - 纳入 SRCADM-02..SRCADM-05 的任何派生库 diff --git a/docs/tasks/SRCADM_01_SURFACEOME_ADMISSION_AUDIT.zh-CN.md b/docs/tasks/SRCADM_01_SURFACEOME_ADMISSION_AUDIT.zh-CN.md new file mode 100644 index 0000000..37d728a --- /dev/null +++ b/docs/tasks/SRCADM_01_SURFACEOME_ADMISSION_AUDIT.zh-CN.md @@ -0,0 +1,115 @@ +# SRCADM-01:`ADC_surfaceome_reference@0.3.0` 来源准入审计 + +- 任务分支:`task_20260805_srcadm-01-surfaceome-admission` +- 授权依据:审计范围 `AUD-01`..`AUD-09` 由 **PR #59** 冻结并获 ChatGPT `APPROVE` +- 机器可读记录:[`../pools/srcadm_01_surfaceome_admission.yaml`](../pools/srcadm_01_surfaceome_admission.yaml),由 `tests/test_srcadm_01_surfaceome_admission.py` 校验 +- 当前状态:**审计已完成,结论待审核** +- 结论:**`admissible_with_conditions`——有条件可纳入,四项条件见第三节** + +## 目的 + +`EVGAP-01` 的抽取被 `SRCADM-01` 阻断:`ADC_surfaceome_reference@0.3.0` 从未被审核,PR #59 明确「派生数据库不能靠自声明 + 哈希纳入」,并冻结了九项必审内容。 + +本文件是那九项的审计结论。**本文件不授予准入**——准入由本 PR 获 `APPROVE` 后成立,届时另开 PR 把 `EVGAP-01` 契约的 `admission_record_ref` 指向审核记录。 + +## 一、九项逐条结论 + +| ID | 审计项 | 结论 | +|---|---|---| +| `AUD-01` | builder 实现与版本 | **PASS** | +| `AUD-02` | raw manifest 与 `raw_manifest_sha256` 的对应 | **PASS**(实算复核) | +| `AUD-03` | 原始来源清单与 release 版本 | **PASS_WITH_FINDING** | +| `AUD-04` | license 与再分发条件 | **PASS_WITH_FINDING** | +| `AUD-05` | evidence family 独立性 | **PASS** | +| `AUD-06` | 来源去重逻辑 | **PASS_WITH_FINDING** | +| `AUD-07` | `discordance_flags` 生成规则 | **PASS** | +| `AUD-08` | 代表性靶点逐行回溯 | **PASS** | +| `AUD-09` | snapshot 可复现性 | **PASS_WITH_FINDING** | + +无一项 `FAIL`。四项带 finding,全部已界定影响范围。 + +## 二、三个最关键的结论 + +### `AUD-05` 独立性成立,而且恰好避开了审核方点名的失效模式 + +审核方要审的是「family independence 是否真正独立,而非同源数据的重复计数」。实测三个家族的来源映射: + +``` +curated_knowledge <- {goa_human, uniprot_reviewed_human} +imaging <- {hpa_subcellular_location} +cell_surface_capture_ms <- {cspa} +``` + +**`goa_human` 与 `uniprot_reviewed_human` 是同源的**——GOA human 由 UniProt 策展流程产出。builder 把二者收进**同一个** `curated_knowledge` 家族,因此不会被计成两个独立家族。**这正是那个失效模式,builder 避开了它。** 实例:`GUCY2C` 两个来源都 `supported`,`family_count` 仍为 **1**。 + +两条进一步的验证: + +1. **family 计数要求支持性证据,不是「有行即计」。** 判据在 builder 第 2112–2118 行,取自 consensus 的 support 布尔值。反例检验:HPA 有行但 `hpa_plasma_membrane = false` 的基因共 **11,334** 个,其中 `imaging` 被计入家族的为 **0 个**。 +2. **`family_count >= 2` 必然包含至少一个实验型家族。** 因为 `curated_knowledge` 是唯一可由两个来源喂养但只计一次的家族,要凑到 2 就必须再有 `imaging` 或 `cell_surface_capture_ms`。所以 `RQ-01` 的两家族门槛不是形式门槛。 + +### `AUD-04` license 歧义存在,但不触及 EVGAP-01 读取的任何字段 + +19 个来源全部声明 license,无缺失。但六个有歧义:`cellphonedb_gene`/`protein`/`complex`/`interaction`(「not declared in cellphonedb…」)、`cellchatdb_human`(GPL-3.0 repository license)、`omnipath_intercell_receptor`(per-resource licenses retained)。 + +**决定性检查:这六个来源没有任何一个出现在 `source_evidence.tsv` 中。** 它们只喂 `cci_receptor_*` 字段,而该字段已被 PR #59 的 `barred_fields` 禁用。 + +进入 `source_evidence.tsv` 的只有四个来源,其中 `goa_human`/`hpa_subcellular_location`/`uniprot_reviewed_human` 为 **CC BY 4.0**。 + +**这个结论是承重的**:它依赖 PR #59 的字段白名单。若白名单日后扩大到 `cci_receptor_*`,本准入必须重新审——已写入 `COND-02`。 + +顺带一处命名不一致:processed 表用 `source_id = cspa`,而 manifest 用 `cspa_validated_surfaceome` 与 `cspa_cell_type_matrix`,两者无法直接 join。 + +### `AUD-09` 完整性可验,但可复现性有边界 + +`shasum -a 256 -c checksums.sha256` 对 raw snapshot 的 **19 个文件全部 OK**。builder 中唯一的时间依赖是第 268 行 `datetime.now()` 用于 `processed_at_utc` 时间戳字段,不参与任何计算;无 `random`、无 `shuffle`。**给定同一 raw snapshot,构建结果确定。** + +**但 raw snapshot 本身对 `uniprot_reviewed_human` 与 `goa_human` 未钉版本**——它们的 release 是 `current_at_download`,只钉下载时刻不钉上游版本。因此从上游重新下载并重建**不能**保证逐字节复现该 snapshot。 + +可复现性成立的前提是**使用已归档的 raw snapshot**,而不是从来源重新获取——已写入 `COND-03`。 + +## 三、四项准入条件 + +任一条被破坏即须重新审。 + +| ID | 条件 | +|---|---| +| `COND-01` | **准入仅限这一个 snapshot**:`0.3.0` / `2026-07-29-quant-topology-mm`,由 19 个 raw 校验和与 4 个 processed 校验和共同钉住。不覆盖其他版本,不覆盖任何后续重建。 | +| `COND-02` | **准入仅限 PR #59 的字段白名单**。license 歧义来源只喂已禁用的 `cci_receptor_*`,这是 `AUD-04` 结论成立的前提。白名单一扩大,准入即失效。 | +| `COND-03` | **准入基于已归档 snapshot,不基于可从上游复现**。两个来源为 `current_at_download`,归档副本即事实来源。 | +| `COND-04` | **重复键不得进入 EVGAP-01 的判据**。已实测 11 个受影响基因全在 41 个靶点之外;若靶点轴日后扩大,须重新检查。 | + +## 四、其余带 finding 的两项 + +**`AUD-03`**:manifest 的 `files` 条目里 `release` 字段**全部为 null**;release 字符串由 builder 另行赋值,且在 processed 表中确有实义取值(`HPA 25.1; Ensembl 109`、`PLOS ONE 2015 supplementary file S2` 等)。但 EVGAP-01 实际使用的四个来源里,`uniprot_reviewed_human` 与 `goa_human` 的 release 是 `current_at_download`,**不是版本号**。后果见 `AUD-09`。 + +**`AUD-06`**:builder 中**检索不到显式去重例程**。实测 `source_evidence.tsv` 在 `(gene_symbol, source_id, evidence_kind)` 上有 **6** 个重复键,`membrane_topology_evidence.tsv` 在 `(gene_symbol, source_id)` 上有 **5** 个;`surfaceome_consensus.tsv` 在 `gene_symbol` 上无重复。 + +影响已界定:受影响基因为 `HERC3`、`MATR3`、`NPIPA9`、`PINX1`、`POLR2J3`、`PRODH`、`ERVK-7`、`NRXN1`、`NRXN2`、`NRXN3`、`SIRPB1`,**没有一个属于 EVGAP-01 的 41 个靶点**。且 family 计数取自 consensus 的 support 布尔值而非行数,故重复行在结构上不可能抬高 `RQ-01` 的家族数;41 个靶点的 `family_count` 与 `families` 列表实测 100% 一致。 + +## 五、`AUD-07` 与 `AUD-08` + +**`AUD-07`**:builder 第 2121–2129 行,四条确定性规则——`hpa_plasma_membrane_uncertain`、`cspa_support_without_uniprot_explicit_surface`、`hpa_only_vs_curated_knowledge`、`generic_membrane_without_surface_localization`。PR #59 的 `E1-04` 把 `discordance_flags` 非空一律判 DEFER,故这四条规则只把冲突显式化并触发 DEFER,**不会产生 RETAIN**。 + +**`AUD-08`**:三个代表性靶点逐行回溯成功,每条主张都落到 `source_id` + `surface_supported` + `source_release` + `source_url`: + +- `CDH17`:`confirmed_surface`,families `cell_surface_capture_ms` + `curated_knowledge`(n=2),ECD-a 路径 +- `CEACAM5`:`confirmed_surface`,families `curated_knowledge` + `imaging`(n=2),ECD-b GPI 路径 +- `GUCY2C`:`supported_surface`,families 仅 `curated_knowledge`(n=1),在 PR #59 规则下落 `E1-02` hold + +## 六、`AUD-01` 的一处残留说明 + +builder 存在、2,721 行、可读,family 计数与 discordance 生成均为显式条件分支,无隐藏启发式。 + +但 `builder_version` 由 config 传入而非脚本内常量,因此「0.3.0」这个版本号依赖 `build_manifest` 的自述,**不能由脚本自身独立确证**。这不构成阻断——snapshot 由 23 个校验和钉住,版本号只是标签——但如实记录。 + +## 七、本审计不做什么 + +不授予准入;不修改 `evgap_01_surface_localization_extraction.yaml` 的 `admission_record_ref`;不执行 `EVGAP-01` 抽取;不执行 Level 01;不解除 `EVGAP-01` 或 `EVGAP-02`;不纳入该数据集的其他版本或后续重建;不扩大 PR #59 的字段白名单;不纳入 `SRCADM-02`..`SRCADM-05` 的任何派生库。 + +## 八、后续顺序 + +1. 本审计 `APPROVE`,在 `logs/` 留下审核记录。 +2. **另开 PR** 把 `EVGAP-01` 契约的 `admission_record_ref` 指向该记录,`authorises_extraction_run` 转为 `true`。 +3. 执行 `EVGAP-01` 抽取 → 结果 PR → binding,解除 `EVGAP-01`。 +4. `EVGAP-02` 的抽取已执行,结果审核在 PR #62;获批后另开 PR 解除 `EVGAP-02`。 +5. **两个缺口都解除后**,才能生成 `ADC_POOL_LEVEL_01_ACCEPTED`。 diff --git a/logs/worklog.md b/logs/worklog.md index e4f24d9..9a0c021 100644 --- a/logs/worklog.md +++ b/logs/worklog.md @@ -2647,3 +2647,24 @@ Purpose: append a detailed timestamped record of what was done, how it was done, - Accepted by reviewer, unchanged: EVGAP-02 与 EVGAP-01/SRCADM-01 独立;覆盖全部 369 pairs;Tier 1/Tier 2 分层与 Tier 2 禁用;A/B/C/D 四类框架;RNA 可支持 linkage 但不满足 LOCK-01;C 类可作 linkage existence 但非 ADC efficacy;disease-level 不自动支持 subgroup;other-cancer 只作 metadata;`L3-05` 可逆非证伪非 killed;不执行 Gate/Level 01、不解除 `EVGAP-01`;不预写 discovery run 结果数量。 - Review write-back: 连接器 403,未写回 GitHub。裁决以人类负责人转述为准,已记录于本条与 handoff 第十四节。 - Next: 推送同一 PR 并同步 PR 描述请求复审。 + +## 2026-08-05T20:30:00-04:00 — SRCADM-01 surfaceome 来源准入审计(审计记录,结论:有条件可纳入) + +- Instruction: 人类负责人指示「起 SRCADM-01」。授权依据是 PR #59 已冻结并获 `APPROVE` 的审计范围 `AUD-01`..`AUD-09`——该范围本身就是这次审计的授权。 +- Method: 实际读取 builder 源码(`AssetGenOS/scripts/build_t7_surfaceome_reference.py`,2,721 行)、raw `download_manifest.json`、`checksums.sha256`、license 声明与三个 processed 表,**而不是描述它们**。所有结论均附可复核依据。 +- Verdicts: `AUD-01` PASS|`AUD-02` PASS(实算)|`AUD-03` PASS_WITH_FINDING|`AUD-04` PASS_WITH_FINDING|`AUD-05` PASS|`AUD-06` PASS_WITH_FINDING|`AUD-07` PASS|`AUD-08` PASS|`AUD-09` PASS_WITH_FINDING。**无一项 FAIL。** 总结论 `admissible_with_conditions`。 +- `AUD-02` hard verification: 对 `raw/2026-07-29-quant-topology-mm/download_manifest.json` 实算 SHA-256 得 `884f419118302ae39c3e50292d03295ff676434868e1061b39ead50f9cc977bb`,与 `build_manifest.json` 声明的 `raw_manifest_sha256` 逐字符一致。 +- `AUD-05` (the item the reviewer singled out) PASS with two reinforcing checks: 三家族映射为 `curated_knowledge <- {goa_human, uniprot_reviewed_human}`/`imaging <- {hpa_subcellular_location}`/`cell_surface_capture_ms <- {cspa}`。**`goa_human` 与 `uniprot_reviewed_human` 同源**(GOA human 由 UniProt 策展流程产出),builder 把二者收进**同一个**家族故不重复计数——`GUCY2C` 两来源皆 supported 而 `family_count` 仍为 1,**这正是审核方点名的失效模式而 builder 避开了它**。加强验证一:family 计数要求**支持性**证据(builder 2112–2118),反例检验 HPA 有行但 `hpa_plasma_membrane=false` 的 **11,334** 个基因中 `imaging` 被计入的为 **0**。加强验证二:`curated_knowledge` 是唯一可由两来源喂养但只计一次的家族,故 `family_count >= 2` 必然含至少一个实验型家族,`RQ-01` 的门槛不是形式门槛。 +- `AUD-04` finding with decisive bound: 19 个来源全部声明 license,六个有歧义(`cellphonedb_gene`/`protein`/`complex`/`interaction`、`cellchatdb_human` GPL-3.0、`omnipath_intercell_receptor` per-resource)。**实测:这六个没有任何一个出现在 `source_evidence.tsv` 中**,只喂已被 #59 `barred_fields` 禁用的 `cci_receptor_*`。进入的四个来源中三个为 CC BY 4.0。该结论**承重**——依赖 #59 字段白名单,白名单扩大即须重审(`COND-02`)。另记一处命名不一致:processed 用 `source_id = cspa`,manifest 用 `cspa_validated_surfaceome`/`cspa_cell_type_matrix`,无法直接 join。 +- `AUD-09` finding: `shasum -a 256 -c checksums.sha256` 对 19 个 raw 文件全部 OK;builder 唯一时间依赖是第 268 行 `datetime.now()` 用于 `processed_at_utc`、不参与计算,无 `random`/`shuffle`,给定同一 snapshot 构建确定。**但 `uniprot_reviewed_human` 与 `goa_human` 的 release 是 `current_at_download`、不是版本号**,从上游重新下载不保证逐字节复现。可复现性成立的前提是使用已归档 snapshot(`COND-03`)。 +- `AUD-03` finding: manifest 的 `files` 条目 `release` 字段**全部为 null**,release 字符串由 builder 另行赋值且在 processed 表中确有实义取值(`HPA 25.1; Ensembl 109`、`PLOS ONE 2015 supplementary file S2` 等);但四个在用来源中两个为 `current_at_download`。 +- `AUD-06` finding with bounded impact: builder 中**检索不到显式去重例程**;实测 `source_evidence.tsv` 在 `(gene_symbol, source_id, evidence_kind)` 上 6 个重复键、`membrane_topology_evidence.tsv` 在 `(gene_symbol, source_id)` 上 5 个,`surfaceome_consensus.tsv` 无重复。受影响基因 `HERC3`/`MATR3`/`NPIPA9`/`PINX1`/`POLR2J3`/`PRODH`/`ERVK-7`/`NRXN1`/`NRXN2`/`NRXN3`/`SIRPB1`,**没有一个属于 41 个靶点**;且 family 计数取自 support 布尔值而非行数,故重复行结构上不可能抬高 `RQ-01`;41 靶点的 `family_count` 与 `families` 列表实测 100% 一致。 +- `AUD-07` PASS: builder 2121–2129 四条确定性规则。#59 的 `E1-04` 把 `discordance_flags` 非空一律判 DEFER,故这些规则只把冲突显式化、**不会产生 RETAIN**。 +- `AUD-08` PASS: 三个代表性靶点逐行回溯成功,每条主张落到 `source_id` + `surface_supported` + `source_release` + `source_url`——`CDH17`(ECD-a,n=2)、`CEACAM5`(ECD-b GPI,n=2)、`GUCY2C`(仅 curated_knowledge,n=1,故落 `E1-02` hold)。 +- `AUD-01` residual recorded honestly: `builder_version` 由 config 传入而非脚本内常量,故「0.3.0」依赖 `build_manifest` 自述、**不能由脚本自身独立确证**。不构成阻断(snapshot 由 23 个校验和钉住),但如实记录。 +- Admission conditions: `COND-01` 仅限该 snapshot|`COND-02` 仅限 #59 字段白名单|`COND-03` 基于已归档 snapshot 而非可从上游复现|`COND-04` 重复键不得进入 EVGAP-01 判据、靶点轴扩大须重查。任一条被破坏即须重审。 +- Validation: `Ran 351 tests` 全部通过(`main` 基线 338 + 新增 13);`scripts/verify_repository_boundary.sh` 通过;`git diff --check` 通过;零 `__pycache__`。测试把结论钉在 #59 冻结范围上——九项 ID 必须与 `required_audit_items` 完全相等、无 FAIL、每项必须有可复核依据、`AUD-02` 摘要必须是 64 位十六进制且与 #59 前缀一致、`AUD-05` 必须点名同源来源对并引用实测反例数、`AUD-04` 依赖的 `cci_receptor_role` 必须确实在 #59 `barred_fields` 中、`AUD-06` 影响界定必须针对 41 靶点轴、本审计不得授予准入。 +- Own test error, self-caught: 初稿从 `source_admission_dependency` 读 `raw_manifest_sha256` 键,而该键在 #59 修订后并不存在(摘要写在 `AUD-02` 条目文本里)。改为跨文件比对前缀并校验 64 位十六进制。 +- Deliberately not done: **未授予准入**;未修改 `evgap_01_surface_localization_extraction.yaml` 的 `admission_record_ref`(仍 `null`,`authorises_extraction_run` 仍 `false`);未执行 `EVGAP-01` 抽取;未执行 Level 01;未解除任何 EVGAP;未纳入该数据集其他版本或后续重建;未扩大 #59 字段白名单;未纳入 `SRCADM-02`..`05`;**未产生任何外部运行产物**(本次是对既有文件的审计);未补九份批准记录。 +- Governance note: **本 PR 不适用 `AGENTS.md`「审核豁免」**,须经 ChatGPT `APPROVE`。 +- Next: 送审;获批后另开 PR 把 `admission_record_ref` 指向审核记录并放行 `EVGAP-01` 抽取。 diff --git a/tests/test_srcadm_01_surfaceome_admission.py b/tests/test_srcadm_01_surfaceome_admission.py new file mode 100644 index 0000000..d3f1f40 --- /dev/null +++ b/tests/test_srcadm_01_surfaceome_admission.py @@ -0,0 +1,199 @@ +"""Verify docs/pools/srcadm_01_surfaceome_admission.yaml. + +The audit record must cover every scope item PR #59 froze, must not grant +admission by itself, and must keep every conclusion tied to a stated, +recheckable basis. These tests read no external database: the audit already ran +and its evidence is recorded in the file under test. +""" + +from __future__ import annotations + +import unittest +from pathlib import Path + +import yaml + + +REPO_ROOT = Path(__file__).resolve().parents[1] +POOLS = REPO_ROOT / "docs" / "pools" +AUDIT_PATH = POOLS / "srcadm_01_surfaceome_admission.yaml" +EVGAP_01_PATH = POOLS / "evgap_01_surface_localization_extraction.yaml" + +VERDICTS = frozenset({"PASS", "PASS_WITH_FINDING", "FAIL"}) + + +def _load(path: Path) -> dict: + with path.open(encoding="utf-8") as handle: + return yaml.safe_load(handle) + + +class Srcadm01AuditTests(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.doc = _load(AUDIT_PATH) + cls.evgap = _load(EVGAP_01_PATH) + cls.findings = {f["id"]: f for f in cls.doc["audit_findings"]} + + def test_audit_targets_the_dependency_evgap_01_registered(self) -> None: + head = self.doc["admission"] + dep = self.evgap["source_admission_dependency"] + self.assertEqual(head["admission_id"], dep["id"]) + self.assertEqual(head["dataset_id"], dep["dataset_id"]) + self.assertEqual(str(head["dataset_version"]), str(dep["dataset_version"])) + self.assertEqual(head["snapshot_id"], dep["snapshot_id"]) + self.assertEqual(head["audit_scope_frozen_by_pr"], 59) + + def test_every_frozen_scope_item_is_answered(self) -> None: + required = {item["id"] for item in + self.evgap["source_admission_dependency"]["required_audit_items"]} + self.assertEqual(set(self.findings), required) + self.assertEqual(self.doc["admission"]["audit_scope_items"], len(required)) + for fid, finding in sorted(self.findings.items()): + with self.subTest(item=fid): + self.assertIn(finding["verdict"], VERDICTS) + self.assertTrue(finding["item"].strip()) + + def test_no_scope_item_failed_outright(self) -> None: + failed = [f for f in self.findings.values() if f["verdict"] == "FAIL"] + self.assertEqual(failed, [], f"failed items: {[f['id'] for f in failed]}") + + def test_every_finding_states_a_recheckable_basis(self) -> None: + """A verdict with no evidence and no finding is an assertion, not an audit.""" + + for fid, finding in sorted(self.findings.items()): + with self.subTest(item=fid): + basis = [k for k in ("evidence", "finding", "integrity_verified") + if str(finding.get(k, "")).strip()] + self.assertTrue(basis, f"{fid} records no basis") + if finding["verdict"] == "PASS_WITH_FINDING": + self.assertTrue(str(finding.get("finding", "")).strip(), + f"{fid} is PASS_WITH_FINDING but names no finding") + + def test_the_audit_does_not_grant_admission(self) -> None: + head = self.doc["admission"] + self.assertEqual(head["status"], "pending_review") + self.assertIs(head["grants_admission_by_itself"], False) + self.assertIsNone(head["admission_record_ref"]) + # EVGAP-01 must still be unfilled and still block its extraction. + dep = self.evgap["source_admission_dependency"] + self.assertIsNone(dep["admission_record_ref"]) + self.assertIs(self.evgap["extraction"]["authorises_extraction_run"], False) + not_authorised = " ".join(self.doc["not_authorised"]) + for phrase in ("授予准入", "admission_record_ref", "EVGAP-01", "Level 01"): + with self.subTest(phrase=phrase): + self.assertIn(phrase, not_authorised) + + def test_recommendation_is_conditional_and_the_conditions_exist(self) -> None: + self.assertEqual(self.doc["admission"]["recommendation"], + "admissible_with_conditions") + conditions = {c["id"]: c for c in self.doc["admission_conditions"]} + self.assertGreaterEqual(len(conditions), 4) + for cid, cond in sorted(conditions.items()): + with self.subTest(condition=cid): + self.assertTrue(cond["condition"].strip()) + self.assertTrue(cond["detail"].strip()) + + def test_family_independence_is_the_load_bearing_pass(self) -> None: + """AUD-05 is the item the reviewer singled out; it must not be hand-waved.""" + + aud = self.findings["AUD-05"] + self.assertEqual(aud["verdict"], "PASS") + self.assertIs(aud["counting_requires_support"], True) + for key in ("same_origin_pair_handled", "counting_requires_support_evidence", + "structural_guarantee"): + with self.subTest(key=key): + self.assertTrue(str(aud[key]).strip()) + # The same-origin pair must be named, not merely alluded to. + text = aud["same_origin_pair_handled"] + self.assertIn("goa_human", text) + self.assertIn("uniprot_reviewed_human", text) + # The counterexample check must cite a measured count. + self.assertIn("11,334", aud["counting_requires_support_evidence"]) + + def test_license_ambiguity_is_bounded_by_the_field_whitelist(self) -> None: + aud = self.findings["AUD-04"] + self.assertEqual(aud["verdict"], "PASS_WITH_FINDING") + self.assertTrue(aud["decisive_check"].strip()) + self.assertTrue(aud["load_bearing_boundary"].strip()) + # The barred field the conclusion depends on must actually be barred there. + barred = {item["field"] for item in self.evgap["barred_fields"]} + self.assertIn("cci_receptor_role", barred) + # And the dependency must be recorded as a condition, not just prose. + conditions = {c["id"]: c for c in self.doc["admission_conditions"]} + whitelist_conditions = [c for c in conditions.values() + if "白名单" in c["condition"] + c["detail"]] + self.assertTrue(whitelist_conditions) + + def test_reproducibility_limit_is_stated_not_glossed(self) -> None: + aud = self.findings["AUD-09"] + self.assertEqual(aud["verdict"], "PASS_WITH_FINDING") + self.assertTrue(aud["integrity_verified"].strip()) + self.assertTrue(aud["determinism"].strip()) + # The finding must name both un-pinned sources and say what that costs, + # not merely mention the phrase somewhere in the paragraph. + finding = aud["finding"] + for token in ("current_at_download", "uniprot_reviewed_human", "goa_human", + "逐字节"): + with self.subTest(token=token): + self.assertIn(token, finding) + # AUD-03 must carry the same limitation rather than contradicting it. + aud_03 = self.findings["AUD-03"]["finding"] + for token in ("current_at_download", "uniprot_reviewed_human", "goa_human"): + with self.subTest(item="AUD-03", token=token): + self.assertIn(token, aud_03) + conditions = " ".join(c["detail"] for c in self.doc["admission_conditions"]) + self.assertIn("current_at_download", conditions) + + def test_dedup_finding_bounds_its_own_impact(self) -> None: + aud = self.findings["AUD-06"] + self.assertEqual(aud["verdict"], "PASS_WITH_FINDING") + bound = aud["bounded_impact"] + self.assertTrue(bound.strip()) + # The bound must exclude the target axis explicitly and name the genes it + # affects, so weakening it to a vague phrase fails. + self.assertIn("没有一个属于", bound) + self.assertIn("41 个靶点", bound) + for gene in ("HERC3", "NRXN1", "SIRPB1"): + with self.subTest(gene=gene): + self.assertIn(gene, bound) + + def test_raw_manifest_digest_was_recomputed_not_copied(self) -> None: + aud = self.findings["AUD-02"] + self.assertEqual(aud["verdict"], "PASS") + self.assertEqual(aud["verification_method"], "recomputed") + # The digest must be the same one EVGAP-01's AUD-02 item points at, and it + # must appear in the evidence as a recomputed value rather than a citation. + item = next(a["item"] for a in + self.evgap["source_admission_dependency"]["required_audit_items"] + if a["id"] == "AUD-02") + prefix = "884f4191" + self.assertIn(prefix, item) + self.assertIn(prefix, aud["evidence"]) + digest = next(w.strip(" ,.;,。") for w in aud["evidence"].split() + if w.startswith(prefix)) + self.assertEqual(len(digest), 64, digest) + self.assertTrue(all(c in "0123456789abcdef" for c in digest)) + + def test_traceback_names_the_targets_it_traced(self) -> None: + aud = self.findings["AUD-08"] + self.assertEqual(aud["verdict"], "PASS") + traced = aud["targets_traced"] + self.assertGreaterEqual(len(traced), 3) + for gene in traced: + with self.subTest(gene=gene): + self.assertIn(gene, aud["evidence"]) + # GUCY2C is the case that contradicts the earlier consensus; keep it visible. + self.assertIn("GUCY2C", traced) + + def test_discordance_rules_only_defer_and_never_retain(self) -> None: + aud = self.findings["AUD-07"] + self.assertEqual(aud["verdict"], "PASS") + self.assertEqual(len(aud["rules"]), 4) + self.assertIn("DEFER", aud["relevance_to_evgap_01"]) + # The EVGAP-01 rule it relies on must in fact defer on discordance. + rule = next(r for r in self.evgap["derivation_rules"] if r["id"] == "E1-04") + self.assertEqual(rule["disposition"], "DEFER") + + +if __name__ == "__main__": + unittest.main() From 49a2b240fd5072e5a8224e4a18a6126745630d7f Mon Sep 17 00:00:00 2001 From: Zhixin Eason Li Date: Thu, 6 Aug 2026 15:40:04 -0400 Subject: [PATCH 2/3] task_20260805: ship a recomputable SRCADM-01 audit bundle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #63 review: the audit's core facts all come from files outside the repository, and the in-repo tests only prove the audit document is internally consistent. That is correct, and it is the same standard I applied to myself in PR #62. The bundle ships the builder source, both manifests, the checksum list, the raw verification result, all three processed tables in full, the 41-target axis, and a verifier. Running verify_audit.py recomputes 48 audit facts from the bundle alone and reports each as MATCH or MISMATCH. All 48 match. The tables are not subset. Subsetting would leave the 11,334 counterexample and the duplicate-key counts unverifiable, putting the reviewer back to trusting the narrative. 2.6 MB compressed is a fair price. One limit stated rather than hidden: the 19 raw files are not shipped, so AUD-09's byte-level verification cannot be redone inside the bundle. Their digests were recomputed at audit time and are recorded. That limit is COND-03, already declared, not a new one. Recomputing found a wording problem. "11,334 genes with an HPA row but hpa_plasma_membrane=false" is right — HPA covers 13,597 genes and 11,334 of them are false — but 18,534 consensus rows carry that value, most because HPA never covered the gene. The phrase "with an HPA row" carried the whole distinction. All three figures are now reported and the test requires them. Under either reading, zero genes wrongly credit the imaging family, so AUD-05 stands. AUD-01's builder path was also ambiguous: it lives in the sibling AssetGenOS repository, not inside StelligenOS. Corrected, and a copy ships in the bundle. Co-Authored-By: Claude Opus 5 --- ...05-srcadm-01-surfaceome-admission.zh-CN.md | 38 +++++++++++ .../pools/srcadm_01_surfaceome_admission.yaml | 67 ++++++++++++++++++- ...ADM_01_SURFACEOME_ADMISSION_AUDIT.zh-CN.md | 48 ++++++++++++- logs/worklog.md | 14 ++++ tests/test_srcadm_01_surfaceome_admission.py | 57 +++++++++++++++- 5 files changed, 218 insertions(+), 6 deletions(-) diff --git a/docs/handoff/2026-08-05-srcadm-01-surfaceome-admission.zh-CN.md b/docs/handoff/2026-08-05-srcadm-01-surfaceome-admission.zh-CN.md index 6105894..164f2c5 100644 --- a/docs/handoff/2026-08-05-srcadm-01-surfaceome-admission.zh-CN.md +++ b/docs/handoff/2026-08-05-srcadm-01-surfaceome-admission.zh-CN.md @@ -9,6 +9,44 @@ - 架构变更:`NO_ARCHITECTURE_CHANGE` - 审核状态:等待 ChatGPT `APPROVE`。**本 PR 不适用 `AGENTS.md`「审核豁免」。** + +## 〇、第一轮审核后补:可独立复核的审计包 + +审核意见:**审计结论的核心事实全部来自仓库外文件;仓库内的测试只验证审计文档自洽, +不能证明外部事实为真。** 这个意见成立,而且与我在 PR #62 对自己提的要求是同一条标准。 + +审计包:`external:result/gen_iet_srcadm_01_audit_bundle_20260806T000000Z` +ZIP SHA-256 `2dbe88af1a2e9aee8004b9cbdd894c48f2f91197726678898aadf5da3f75e931`, +2,663,987 bytes,13 个条目。 + +解包后 `python3 verify_audit.py .` 即重算 **48 项**审计事实,逐项 `MATCH`/`MISMATCH`。 +**本次 48/48 全部 `MATCH`。** + +三张 processed 表**未做子集裁剪**——子集会使 11,334、6/5 重复键等计数无法重算, +审核方就只能重新相信叙述。整包压缩后 2.6 MB,代价可接受。 + +### 唯一不能在包内独立重算的项 + +19 个 raw 文件未随包提供(合计数 GB)。其校验和在审计时已实算,记于 +`raw_checksum_verification.json`(**19/19 `OK`**)。独立重算需归档 snapshot 本身—— +**这正是 `COND-03` 已声明的边界**,不是新增限制。`download_manifest.json` 随包提供, +故 `AUD-02` 可在包内完整重算。 + +### 重算过程中查出的一处表述问题 + +原文「HPA 有行但 `hpa_plasma_membrane = false` 的基因共 **11,334** 个」。 +重算脚本首版按「consensus 中该字段为 false」计数,得 **18,534**。 + +**11,334 是对的**:HPA 实际覆盖 **13,597** 个基因,其中 11,334 个该字段为 `false`; +多出的部分是**从未被 HPA 覆盖**的基因——该字段对它们同样是 `false`。 +原文的「有行」二字承载了全部区分度,容易读漏。现在机器可读记录与脚本都同时报出 +13,597/18,534/11,334。**两种口径下 `imaging` 被错误计入的都是 0 个**,`AUD-05` 结论不变。 + +### 另一处更正 + +`AUD-01` 原写 `AssetGenOS/scripts/build_t7_surfaceome_reference.py`,有歧义—— +该路径**在 StelligenOS 仓库之外**,是同级的 `AssetGenOS` 仓库。已更正,副本随包提供。 + ## 一、本次范围 人类负责人指示起 `SRCADM-01`。这是 Track B 的第一环,也是唯一挡住 `EVGAP-01` 的一环。 diff --git a/docs/pools/srcadm_01_surfaceome_admission.yaml b/docs/pools/srcadm_01_surfaceome_admission.yaml index 7654c29..397ce14 100644 --- a/docs/pools/srcadm_01_surfaceome_admission.yaml +++ b/docs/pools/srcadm_01_surfaceome_admission.yaml @@ -21,13 +21,68 @@ admission: admission_record_ref: null next_step: 获 APPROVE 后另开 PR 把 EVGAP-01 契约的 admission_record_ref 指向审核记录 +# 可独立复核的审计包。PR #63 第一轮审核指出:审计结论的核心事实全部来自仓库外文件, +# 仅凭文档自洽的测试无法证明外部事实为真。该意见成立——本节即其应答。 +audit_bundle: + bundle_id: external:result/gen_iet_srcadm_01_audit_bundle_20260806T000000Z + package: gen_iet_srcadm_01_audit_bundle_20260806T000000Z.zip + package_sha256: 2dbe88af1a2e9aee8004b9cbdd894c48f2f91197726678898aadf5da3f75e931 + package_bytes: 2663987 + entries: 13 + read_only: true + grants_admission: false + # 复核方式:解包后运行 python3 verify_audit.py . + reverification_command: python3 verify_audit.py . + recomputed_checks: 48 + recomputed_result: all_match + contents: + - file: build_t7_surfaceome_reference.py + serves: [AUD-01, AUD-05, AUD-06, AUD-07, AUD-09] + - file: build_manifest.json + serves: [AUD-01, AUD-02] + - file: download_manifest.json + serves: [AUD-02, AUD-03, AUD-04] + - file: checksums.sha256 + serves: [AUD-09] + - file: raw_checksum_verification.json + serves: [AUD-09] + - file: source_evidence.tsv + serves: [AUD-03, AUD-04, AUD-05, AUD-06, AUD-08] + - file: membrane_topology_evidence.tsv + serves: [AUD-06] + - file: surfaceome_consensus.tsv + serves: [AUD-05, AUD-06, AUD-07, AUD-08] + - file: evgap_01_target_axis.txt + serves: [AUD-06] + - file: verify_audit.py + serves: [all] + - file: audit_report.json + serves: [all] + # 三张 processed 表未做子集裁剪,故所有计数可精确重算,而非只能核对代表性行。 + processed_tables_subset: false + processed_tables_subset_reason: >- + 子集会使 11,334、6/5 重复键等计数无法重算,审核方只能重新相信叙述。 + # 唯一不能在包内独立重算的项,如实写明。 + not_independently_recomputable_in_bundle: + - item: AUD-09 的 19 个 raw 文件逐字节校验 + reason: raw 文件合计数 GB,未随包提供 + what_is_provided: >- + checksums.sha256 与审计时实算结果 raw_checksum_verification.json(19/19 OK) + full_recomputation_requires: 归档 snapshot 本身 + already_stated_as: COND-03 + - item: AUD-02 的 SHA-256 对应关系 + reason: 无此限制——download_manifest.json 随包提供,可在包内完整重算 + note: 列此条是为免误读为同样受限 + # 逐项审计结论。每一条都必须有可复核的依据,不接受「已阅读」这类断言。 audit_findings: - id: AUD-01 item: builder 实现与版本 verdict: PASS evidence: >- - AssetGenOS/scripts/build_t7_surfaceome_reference.py 存在,2,721 行,可读。 + /Volumes/Stelligen_SSD/Stelligen/AssetGenOS/scripts/build_t7_surfaceome_reference.py + 存在,2,721 行,可读。**注意该路径在 StelligenOS 仓库之外**,是同级的 AssetGenOS + 仓库;首版审计只写 AssetGenOS/scripts/... 有歧义,已更正。副本随审计包提供。 family 计数、discordance 生成、consensus 打分均为显式条件分支,无隐藏启发式。 residual_note: >- builder_version 由 config 传入而非脚本内常量,故「0.3.0」这个版本号依赖 @@ -93,8 +148,14 @@ audit_findings: counting_requires_support: true counting_requires_support_evidence: >- family 由 consensus 的 support 布尔值推出(builder 第 2112–2118 行), - 不是「有行即计」。实测反例检验:HPA 有行但 hpa_plasma_membrane=false 的基因 - 共 11,334 个,其中 imaging 被计入家族的为 **0 个**。 + 不是「有行即计」。实测反例检验:**HPA 实际覆盖 13,597 个基因** + (在 source_evidence.tsv 中有 hpa_subcellular_location 行), + 其中 hpa_plasma_membrane = false 的为 **11,334 个**, + 而 imaging 被计入家族的为 **0 个**。 + 口径说明:consensus 中 hpa_plasma_membrane = false 的行共 18,534 条, + 但其中包含**从未被 HPA 覆盖**的基因——该字段对它们同样是 false。 + 11,334 这个数只统计 HPA 确有覆盖的基因,「有行」二字承载全部区分度。 + 两种口径下 imaging 被错误计入的基因都是 0 个。 structural_guarantee: >- 由于 curated_knowledge 是唯一可由两个来源喂养但只计一次的家族, family_count >= 2 必然包含至少一个实验型家族(imaging 或 diff --git a/docs/tasks/SRCADM_01_SURFACEOME_ADMISSION_AUDIT.zh-CN.md b/docs/tasks/SRCADM_01_SURFACEOME_ADMISSION_AUDIT.zh-CN.md index 37d728a..6801297 100644 --- a/docs/tasks/SRCADM_01_SURFACEOME_ADMISSION_AUDIT.zh-CN.md +++ b/docs/tasks/SRCADM_01_SURFACEOME_ADMISSION_AUDIT.zh-CN.md @@ -4,6 +4,8 @@ - 授权依据:审计范围 `AUD-01`..`AUD-09` 由 **PR #59** 冻结并获 ChatGPT `APPROVE` - 机器可读记录:[`../pools/srcadm_01_surfaceome_admission.yaml`](../pools/srcadm_01_surfaceome_admission.yaml),由 `tests/test_srcadm_01_surfaceome_admission.py` 校验 - 当前状态:**审计已完成,结论待审核** +- 可独立复核的审计包:`external:result/gen_iet_srcadm_01_audit_bundle_20260806T000000Z`, + ZIP SHA-256 `2dbe88af1a2e9aee8004b9cbdd894c48f2f91197726678898aadf5da3f75e931` - 结论:**`admissible_with_conditions`——有条件可纳入,四项条件见第三节** ## 目的 @@ -12,6 +14,50 @@ 本文件是那九项的审计结论。**本文件不授予准入**——准入由本 PR 获 `APPROVE` 后成立,届时另开 PR 把 `EVGAP-01` 契约的 `admission_record_ref` 指向审核记录。 +## 〇、审计包(第一轮审核后补) + +第一轮审核指出:**审计结论的核心事实全部来自仓库外文件,而仓库内的测试只验证审计文档 +自洽,不能证明外部事实为真。** 这个意见成立——所以本轮补一个只读复核包。 + +解包后运行: + +``` +python3 verify_audit.py . +``` + +脚本只读包内文件,**重算 48 项审计事实**,逐项输出 `MATCH` / `MISMATCH`。 +本次结果:**48 / 48 全部 `MATCH`**。 + +三张 processed 表**未做子集裁剪**(`source_evidence` 11.8 MB、`surfaceome_consensus` 8.9 MB、 +`membrane_topology_evidence` 3.5 MB),因此 11,334、6/5 重复键、家族映射等数字都能**精确重算**, +而不是只能核对代表性行。整包压缩后 2.6 MB。 + +### 唯一不能在包内独立重算的一项 + +**19 个 raw 文件未随包提供**(合计数 GB)。其校验和在审计时已从归档 snapshot 实算, +结果记在 `raw_checksum_verification.json`(**19/19 `OK`**)。要独立重算这一项需要归档 +snapshot 本身——**这正是 `COND-03` 已声明的边界**,不是新增限制。 + +`download_manifest.json` 本身随包提供,故 **`AUD-02` 的 SHA-256 对应关系可在包内完整重算**。 + +### 重算过程中发现的一处表述问题 + +审计原文写「HPA 有行但 `hpa_plasma_membrane = false` 的基因共 **11,334** 个」。 +重算脚本首版按「consensus 中 `hpa_plasma_membrane = false`」计数,得 **18,534**。 + +**11,334 这个数字是对的**:HPA 实际覆盖 **13,597** 个基因,其中 11,334 个 `plasma_membrane = false`。 +18,534 里多出的部分是**从未被 HPA 覆盖**的基因——该字段对它们同样是 `false`。 + +原文的「有行」二字承载了全部区分度,容易读漏。现已在机器可读记录与脚本中同时报出 +13,597/18,534/11,334 三个数。**两种口径下 `imaging` 被错误计入的基因都是 0 个**, +故 `AUD-05` 的结论不变。 + +### 另一处更正 + +`AUD-01` 原写「`AssetGenOS/scripts/build_t7_surfaceome_reference.py`」有歧义—— +该路径**在 StelligenOS 仓库之外**,是同级的 `AssetGenOS` 仓库 +(`/Volumes/Stelligen_SSD/Stelligen/AssetGenOS/scripts/`)。已更正,副本随包提供。 + ## 一、九项逐条结论 | ID | 审计项 | 结论 | @@ -108,7 +154,7 @@ builder 存在、2,721 行、可读,family 计数与 discordance 生成均为 ## 八、后续顺序 -1. 本审计 `APPROVE`,在 `logs/` 留下审核记录。 +1. 本审计连同审计包 `APPROVE`,在 `logs/` 留下审核记录。 2. **另开 PR** 把 `EVGAP-01` 契约的 `admission_record_ref` 指向该记录,`authorises_extraction_run` 转为 `true`。 3. 执行 `EVGAP-01` 抽取 → 结果 PR → binding,解除 `EVGAP-01`。 4. `EVGAP-02` 的抽取已执行,结果审核在 PR #62;获批后另开 PR 解除 `EVGAP-02`。 diff --git a/logs/worklog.md b/logs/worklog.md index 9a0c021..b7b0f4f 100644 --- a/logs/worklog.md +++ b/logs/worklog.md @@ -2668,3 +2668,17 @@ Purpose: append a detailed timestamped record of what was done, how it was done, - Deliberately not done: **未授予准入**;未修改 `evgap_01_surface_localization_extraction.yaml` 的 `admission_record_ref`(仍 `null`,`authorises_extraction_run` 仍 `false`);未执行 `EVGAP-01` 抽取;未执行 Level 01;未解除任何 EVGAP;未纳入该数据集其他版本或后续重建;未扩大 #59 字段白名单;未纳入 `SRCADM-02`..`05`;**未产生任何外部运行产物**(本次是对既有文件的审计);未补九份批准记录。 - Governance note: **本 PR 不适用 `AGENTS.md`「审核豁免」**,须经 ChatGPT `APPROVE`。 - Next: 送审;获批后另开 PR 把 `admission_record_ref` 指向审核记录并放行 `EVGAP-01` 抽取。 + +### 2026-08-05 20:20 EDT — SRCADM-01:补可独立复核的审计包(PR #63 第二轮) + +- Trigger: ChatGPT 对 PR #63 `REQUEST_CHANGES`——审计结论的核心事实全部来自仓库外文件,仓库内测试只验证文档自洽,**不能证明外部事实为真**。意见成立,且与我在 PR #62 对自己提的标准是同一条。 +- Bundle: `external:result/gen_iet_srcadm_01_audit_bundle_20260806T000000Z`,ZIP SHA-256 `2dbe88af1a2e9aee8004b9cbdd894c48f2f91197726678898aadf5da3f75e931`,2,663,987 bytes,13 个条目。含 builder 源码、`build_manifest.json`、`download_manifest.json`、`checksums.sha256`、raw 校验实算结果、三张完整 processed 表、41 靶点轴、`verify_audit.py`、`audit_report.json`。 +- Not subset: 三张 processed 表**未做子集裁剪**(11.8/8.9/3.5 MB)。子集会使 11,334、6/5 重复键等计数无法重算,审核方只能重新相信叙述。压缩后 2.6 MB,代价可接受。 +- Re-verification: `python3 verify_audit.py .` 重算 **48 项**审计事实,**48/48 `MATCH`**。脚本只读包内文件。 +- Stated limit: 19 个 raw 文件未随包提供(数 GB),其校验和审计时已实算(19/19 `OK`)记于 `raw_checksum_verification.json`。独立重算需归档 snapshot 本身——**这正是 `COND-03` 已声明的边界**,不是新增限制。`download_manifest.json` 随包提供,故 `AUD-02` 可在包内完整重算。 +- Found while recomputing: 原文「HPA 有行但 `hpa_plasma_membrane = false` 共 11,334 个」——脚本首版按「consensus 中该字段为 false」计数得 **18,534**。**11,334 是对的**:HPA 实际覆盖 **13,597** 个基因,其中 11,334 个为 false;多出部分是**从未被 HPA 覆盖**的基因,该字段对它们同样是 false。「有行」二字承载全部区分度,容易读漏。现三个数一并报出,测试强制三者同时出现。**两种口径下 `imaging` 被错误计入的都是 0 个**,`AUD-05` 结论不变。 +- Also corrected: `AUD-01` 原写 `AssetGenOS/scripts/...` 有歧义——该路径**在 StelligenOS 仓库之外**,是同级 `AssetGenOS` 仓库。已更正,副本随包提供。 +- Tests: `tests/test_srcadm_01_surfaceome_admission.py` 由 13 增至 **15**(新增:包可重算且不自授准入、包如实声明其不能证明什么);全库 `Ran 353 tests` OK;boundary check 通过。 +- Mutation testing: 12 个变异,9 个被捕获。**3 个「逃逸」经查是变异本身不破坏被测不变式**——单独从某个条目删除 `AUD-02` 时另一条目仍服务该项,而测试断言的是「至少一个文件服务该项」;从**所有**条目删除即被捕获。同时把该断言的三元表达式改写清楚,原写法可读性差、`{"all"}` 并集无实际作用。 +- Deliberately not done: 未授予准入(`status: pending_review`、`admission_record_ref: null` 未动);未修改 `EVGAP-01` 契约;未执行任何抽取。 +- Next: 送审审计包;获批后另开 PR 填 `admission_record_ref` 并放行 `EVGAP-01` 抽取。 diff --git a/tests/test_srcadm_01_surfaceome_admission.py b/tests/test_srcadm_01_surfaceome_admission.py index d3f1f40..80aa2cb 100644 --- a/tests/test_srcadm_01_surfaceome_admission.py +++ b/tests/test_srcadm_01_surfaceome_admission.py @@ -107,8 +107,61 @@ def test_family_independence_is_the_load_bearing_pass(self) -> None: text = aud["same_origin_pair_handled"] self.assertIn("goa_human", text) self.assertIn("uniprot_reviewed_human", text) - # The counterexample check must cite a measured count. - self.assertIn("11,334", aud["counting_requires_support_evidence"]) + # The counterexample check must cite a measured count, and it must state + # the denominator. "11,334 genes with plasma_membrane=false" is ambiguous + # on its own: 18,534 consensus rows carry that value, most because HPA + # never covered the gene at all. Both figures must appear so the reader + # cannot conflate them. + counterexample = aud["counting_requires_support_evidence"] + for figure in ("13,597", "11,334", "18,534"): + with self.subTest(figure=figure): + self.assertIn(figure, counterexample) + self.assertIn("从未被 HPA 覆盖", counterexample) + + def test_the_audit_ships_a_bundle_that_can_be_recomputed(self) -> None: + """Tests on this file prove only internal consistency. The external facts + need an external, re-runnable bundle.""" + + bundle = self.doc["audit_bundle"] + self.assertIs(bundle["read_only"], True) + self.assertIs(bundle["grants_admission"], False) + self.assertTrue(bundle["reverification_command"].strip()) + self.assertEqual(bundle["recomputed_result"], "all_match") + self.assertGreaterEqual(bundle["recomputed_checks"], 40) + # A 64-hex package digest, so a swapped package is detectable. + digest = bundle["package_sha256"] + self.assertEqual(len(digest), 64, digest) + self.assertTrue(all(c in "0123456789abcdef" for c in digest)) + # Every scope item must be served by at least one shipped file. The + # literal "all" marks the verifier and its report, which serve every + # item; it is not itself a scope item, so drop it before comparing. + served = {item for entry in bundle["contents"] for item in entry["serves"]} + self.assertIn("all", served, "no file is declared as serving every item") + served.discard("all") + required = {item["id"] for item in + self.evgap["source_admission_dependency"]["required_audit_items"]} + self.assertEqual(required - served, set(), + f"scope items with no bundle file: {sorted(required - served)}") + + def test_the_bundle_states_what_it_cannot_prove(self) -> None: + """A bundle that implies everything is recomputable would be worse than + no bundle.""" + + bundle = self.doc["audit_bundle"] + # Full tables, not representative rows — otherwise the counts are + # unverifiable and the reviewer is back to trusting the narrative. + self.assertIs(bundle["processed_tables_subset"], False) + self.assertTrue(bundle["processed_tables_subset_reason"].strip()) + limits = bundle["not_independently_recomputable_in_bundle"] + self.assertTrue(limits) + raw_limit = next(item for item in limits if "AUD-09" in item["item"]) + self.assertTrue(raw_limit["reason"].strip()) + self.assertTrue(raw_limit["what_is_provided"].strip()) + # The limitation must be the one the conditions already declare, not a + # new and unrecorded one. + self.assertEqual(raw_limit["already_stated_as"], "COND-03") + conditions = {c["id"] for c in self.doc["admission_conditions"]} + self.assertIn("COND-03", conditions) def test_license_ambiguity_is_bounded_by_the_field_whitelist(self) -> None: aud = self.findings["AUD-04"] From ae4dca32ff06f1a77e2a4e4dcc13dd6b16261eeb Mon Sep 17 00:00:00 2001 From: Zhixin Eason Li Date: Thu, 6 Aug 2026 16:07:45 -0400 Subject: [PATCH 3/3] No semantic or architectural changes. This revision only provides the exact external review package requested in the previous review. Co-Authored-By: Claude Opus 5 --- ...05-srcadm-01-surfaceome-admission.zh-CN.md | 22 +++++++++++--- .../pools/srcadm_01_surfaceome_admission.yaml | 29 +++++++++++++++---- logs/worklog.md | 13 +++++++++ 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/docs/handoff/2026-08-05-srcadm-01-surfaceome-admission.zh-CN.md b/docs/handoff/2026-08-05-srcadm-01-surfaceome-admission.zh-CN.md index 164f2c5..371efa1 100644 --- a/docs/handoff/2026-08-05-srcadm-01-surfaceome-admission.zh-CN.md +++ b/docs/handoff/2026-08-05-srcadm-01-surfaceome-admission.zh-CN.md @@ -16,11 +16,25 @@ 不能证明外部事实为真。** 这个意见成立,而且与我在 PR #62 对自己提的要求是同一条标准。 审计包:`external:result/gen_iet_srcadm_01_audit_bundle_20260806T000000Z` -ZIP SHA-256 `2dbe88af1a2e9aee8004b9cbdd894c48f2f91197726678898aadf5da3f75e931`, -2,663,987 bytes,13 个条目。 +ZIP SHA-256 `49d56c395661e7c71ba4caa60657126596cf4f784430ff462ab4512fdb0237b4`, +2,666,041 bytes,**13 个文件**(ZIP 内 14 个条目,多出的一条是目录条目)。 -解包后 `python3 verify_audit.py .` 即重算 **48 项**审计事实,逐项 `MATCH`/`MISMATCH`。 -**本次 48/48 全部 `MATCH`。** +**上一版声明的 `2dbe88af…` 作废**——按复核要求补入了 `audit_expected.json`、 +`license_manifest.json` 与更多重算项,包内容变了哈希必然变。**以此为准。** + +解包后 `python3 verify_audit.py .` 即重算 **72 项**审计事实,逐项 `MATCH`/`MISMATCH`, +末行输出 `72/72 MATCH`。无网络、无写入、不依赖包外路径,解压即可运行,退出码 `0` 表示全通过。 + +**从全新解压目录实测:`72/72 MATCH`,退出码 0。** + +**为什么是 72 而不是上一版的 48。** 按复核要求补入了 processed 表逐文件 SHA-256/字节数/行数 +重算(9 项)、六个 license 歧义来源逐一验证其 license 文本确实歧义(6 项)、 +三个 CC BY 4.0 来源逐一验证(3 项)、license 清单覆盖全部 19 个来源、 +已记录 raw 摘要与 `checksums.sha256` 逐条一致、snapshot 与 dataset 版本一致性、 +target 轴规模等。**未删除任何原有检查**,48 项全部仍在。 + +**判据不来自预写结论。** 被审计主张放在 `audit_expected.json`,脚本从 builder 源码与 +三张表**重新算出**每个数字再比对;那份文件里写错一个数,这里就会 `MISMATCH`。 三张 processed 表**未做子集裁剪**——子集会使 11,334、6/5 重复键等计数无法重算, 审核方就只能重新相信叙述。整包压缩后 2.6 MB,代价可接受。 diff --git a/docs/pools/srcadm_01_surfaceome_admission.yaml b/docs/pools/srcadm_01_surfaceome_admission.yaml index 397ce14..afd332c 100644 --- a/docs/pools/srcadm_01_surfaceome_admission.yaml +++ b/docs/pools/srcadm_01_surfaceome_admission.yaml @@ -26,15 +26,30 @@ admission: audit_bundle: bundle_id: external:result/gen_iet_srcadm_01_audit_bundle_20260806T000000Z package: gen_iet_srcadm_01_audit_bundle_20260806T000000Z.zip - package_sha256: 2dbe88af1a2e9aee8004b9cbdd894c48f2f91197726678898aadf5da3f75e931 - package_bytes: 2663987 - entries: 13 + package_sha256: 49d56c395661e7c71ba4caa60657126596cf4f784430ff462ab4512fdb0237b4 + package_bytes: 2666041 + files: 13 + zip_entries: 14 read_only: true grants_admission: false # 复核方式:解包后运行 python3 verify_audit.py . reverification_command: python3 verify_audit.py . - recomputed_checks: 48 + recomputed_checks: 72 recomputed_result: all_match + # 判据不来自预写结论:主张放在 audit_expected.json,脚本从 builder 源码与三张表 + # 重新算出每个数字再比对。写错一个数就会 MISMATCH,不会静默通过。 + verdicts_read_from_file: false + expected_values_file: audit_expected.json + verifier_properties: + network_access: false + writes_files: false + depends_on_paths_outside_bundle: false + runnable_after_extract: true + exit_zero_means_all_passed: true + final_line: 72/72 MATCH + # 上一版为 48 项。本轮按复核要求补入 processed 表逐文件摘要/字节数/行数重算、 + # 六个 license 歧义来源逐一验证、三个 CC BY 4.0 来源逐一验证等,未删除任何原有检查。 + previous_recomputed_checks: 48 contents: - file: build_t7_surfaceome_reference.py serves: [AUD-01, AUD-05, AUD-06, AUD-07, AUD-09] @@ -56,7 +71,11 @@ audit_bundle: serves: [AUD-06] - file: verify_audit.py serves: [all] - - file: audit_report.json + - file: audit_expected.json + serves: [all] + - file: license_manifest.json + serves: [AUD-04] + - file: README.md serves: [all] # 三张 processed 表未做子集裁剪,故所有计数可精确重算,而非只能核对代表性行。 processed_tables_subset: false diff --git a/logs/worklog.md b/logs/worklog.md index aa0c3bc..8fa439a 100644 --- a/logs/worklog.md +++ b/logs/worklog.md @@ -2690,3 +2690,16 @@ Purpose: append a detailed timestamped record of what was done, how it was done, - Versioning: Updated the version index to state that unapproved `v2-draft` was superseded without a snapshot; no `v3` snapshot is created before approval. Updated architecture/README navigation, added this repository handoff, and refreshed workspace `HANDOFF.md` with the active v3 review task. - Boundary: Documentation-only. No data, cache, result, model weight, provider execution, Gate execution, contract change, lifecycle change, or module implementation was added. - Validation: Final branch validation ran 338 tests successfully; `scripts/verify_repository_boundary.sh` and `git diff --check` passed; no `__pycache__` directory was generated. + +### 2026-08-06 16:35 EDT — SRCADM-01:冻结语义,只补正式审计包(PR #63 第三轮) + +- Instruction: 冻结当前 HEAD,不改九项审计与四项条件,只上传正式审计包并使三处声明一致。 +- **本轮未改任何语义**:九项 `audit_findings` 的 verdict 与依据未动、四项 `admission_conditions` 未动、`status: pending_review` 未动、`admission_record_ref: null` 未动、`EVGAP-01` 的 `authorises_extraction_run` 仍为 `false`、未顺带授予 admission。 +- Added to the bundle only: `audit_expected.json`、`license_manifest.json`,并重写 `verify_audit.py`;移除 `audit_report.json`(预写结论不应与证据混放)。因包内容变化,ZIP 哈希必然变化。 +- Package: `gen_iet_srcadm_01_audit_bundle_20260806T000000Z.zip`,SHA-256 `49d56c395661e7c71ba4caa60657126596cf4f784430ff462ab4512fdb0237b4`,2,666,041 bytes,**13 个文件**(ZIP 内 14 个条目,多出的一条是目录条目)。**上一版声明的 `2dbe88af…` 作废。** +- Verifier properties: 无网络、无写入、不依赖包外路径、解压即可运行、退出码 0 表示全通过、逐项输出 `MATCH`/`MISMATCH`、末行 `72/72 MATCH`。 +- **判据不来自预写结论**:主张放在 `audit_expected.json`,脚本从 builder 源码与三张表**重新算出**每个数字再比对;那份文件写错一个数就会 `MISMATCH`。已在记录中以 `verdicts_read_from_file: false` 固定该性质。 +- **72 而不是 48,如实报告**:按复核要求补入 processed 表逐文件 SHA-256/字节数/行数重算(9 项)、六个 license 歧义来源逐一验证其文本确实歧义(6 项)、三个 CC BY 4.0 来源逐一验证(3 项)、license 清单覆盖 19 个来源、已记录 raw 摘要与 `checksums.sha256` 逐条一致、snapshot 与 dataset 版本一致性、target 轴规模等。**未删除任何原有检查**,48 项全部仍在。上一版的 48 记为 `previous_recomputed_checks`。 +- Covered by recomputation, as requested: raw manifest SHA-256;processed 三表摘要;family source mapping;GOA 与 UniProt 同属一个 family;HPA 反例 11,334(并同时报出 13,597 与 18,534 两个口径);imaging 误计为 0;source_evidence 重复键 6;topology 重复键 5;11 个受影响基因;与 41-target 轴交集为空;CDH17/CEACAM5/GUCY2C provenance;license 歧义来源未进入允许字段;consensus `gene_symbol` 唯一;41 个 target 的 `family_count` 与 family list 一致。 +- Verified from a clean extract in a temp directory, not the working copy: **`72/72 MATCH`,退出码 0**。 +- Repo-side changes this round: 仅本条 worklog、handoff 的包信息段、YAML 的 `audit_bundle` 数据字段(哈希/字节/文件数/检查数/内容清单)。九项审计结论、四项条件、测试逻辑零改动。