You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

9.9 KiB

W1-T3 最终独立复审报告(第二轮修复)

状态

完成。本次复审只读取实现、迁移、来源清单、前两轮报告和本地/官方 CSV,并独立执行指定测试、来源审计及 ESCO URI 只读核验;未修改任何实现文件,仅写入本报告。

Verdict

FAIL

第二轮唯一 Important finding 尚未完全关闭:

  • 47 条 O*NET software_skills 来源当前数据全部真实,但测试只验证“每条 DB 三元组存在于快照集合中”,没有验证“每个技能代码对应唯一正确的三元组”;交换两个未被单点断言的技能来源后,测试仍会通过。
  • 137 条非空 Importance 的值均已逐条核对,但测试对 importance_is_proxy 仍主要是 0/1 枚举检查,仅 10 条做 proxy=1 精确断言;将其余 127 条中的任一 1 改为 0 后,测试仍会通过。

以上属于回归保护缺口,不阻断当前种子数据的正确性,但按 TO-12 和本轮“篡改必须失败”的要求,仍阻断 T4。

独立测试结果

环境:

  • JAVA_HOME=D:\java\jdk
  • Maven:D:\maven\apache-maven-3.9.7\bin\mvn.cmd
  • 工作目录:D:\1\zhi\zhilai\mydreamjob-server

执行:

$env:JAVA_HOME='D:\java\jdk'
& 'D:\maven\apache-maven-3.9.7\bin\mvn.cmd' '-Dtest=SkillTaxonomyMigrationTest,SkillCloudSeedDataIntegrationTest' test

结果:

SkillCloudSeedDataIntegrationTest: Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
SkillTaxonomyMigrationTest:       Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
Total:                             Tests run: 9, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS
Total time: 39.800 s

来源审计方法与结果

1. O*NET 31.0 官方来源核验

独立下载官方发布包:

  • URL:https://www.onetcenter.org/dl_files/database/db_31_0_csv.zip
  • HTTP:200
  • 文件大小:16,237,378 bytes
  • HTTP Last-Modified:Tue, 25 Aug 2026 03:46:00 GMT
  • 官方 ZIP SHA-256:55033FC68B4C13EC23E7F74DC6378660F6E854E75D55D6E333AE0A761D3987CD

从官方 ZIP 解压后与 mydreamjob-server/target/onet-31 逐文件比较,SHA-256 完全一致:

文件 SHA-256
software_skills.csv 6AABB96B464288DB849580E6510530EFFF3F80E25DDE2BB23F1FC36BB526B016
work_activities.csv 6C266205B2926C0C854A7A577AAE29FE492C71BB0DF7E197ADCA830B58FE4EA1
knowledge.csv 88092E17D16096CDEBE292FEE4389DEB460EDDA24AE8B4637212C69C71562F55
essential_skills.csv 68C4D31D8C55077238395DCA40CC4E6D32E6A608B5474BB8264B1F98AE415BD7
transferable_skills.csv F17D0419E818A3BF2F55076510C76C4AEF01CC8F5AEE2670D2F44F6F44AC60F7
occupation_data.csv A09EAE1D6609686E44E05B7290993A1C8B523D8CA224BC0EEDC194C855C3EE02

因此,target/onet-31 中的本轮 CSV 来源与官方 O*NET 31.0 发布包一致,不是本地改写版本。

2. 证据快照与官方 CSV 对照

mydreamjob-server/src/test/resources/skill-cloud/onet-31-reference.csv:

  • 总行数:92 条数据行。
  • SOFTWARE:47 条,47 个唯一键,无重复。
  • IMPORTANCE:45 条唯一 SOC + Element ID 组合,无重复。
  • 47/47 条 SOFTWARE 三元组均精确命中 software_skills.csv。
  • 45/45 条 IMPORTANCE 均命中声明的官方 CSV,Data Value 精确一致。
  • 未发现无依据行、伪造三元组或伪造数值。
  • SOFTWARE 的 data_value 均为空,符合 software_skills 没有 Importance 值的结构。

结论:当前证据快照确实来自 O*NET 31.0 官方 CSV,数据与官方 CSV 完全一致。

3. V36 数据全量审计

独立解析 V36 后确认:

项目 结果
O*NET 技能 47
DB 使用的 SOFTWARE 三元组集合与快照集合相等 是
职位技能关系 139
非空 Importance 137
空 Importance 2
非空关系正确命中快照键和值 137/137
非空关系当前 importance_is_proxy=1 137/137
空关系的 SOC/Element ID 为 NULL、proxy=0 2/2
137 条关系使用的唯一 Importance 键 45
快照 IMPORTANCE 键集合与实际使用集合相等 是

结论:当前数据层没有发现伪造来源、错误数值或错误代理标记。

4. Manifest 分类覆盖

从 source-manifest.md 独立解析 10 个职位及声明分类,并与 V36 对比:

  • 职位:10/10 一致。
  • 分类:68/68 存在。
  • 每个声明分类至少关联 1 条技能关系;本轮独立统计最小值为 2、最大值为 3。
  • 未发现分类遗漏或职位—分类清单不一致。

5. ESCO 职位 URI

V36 与测试中的 10 个职位映射一致,其中 8 个唯一 ESCO occupation URI。对 8 个 URI 调用 ESCO 官方只读 API,全部返回 HTTP 200:

  • 前端开发工程师 URI
  • 后端开发工程师 URI
  • 测试工程师 URI
  • 金融数据分析师 URI
  • 风控建模工程师 URI
  • 教育产品经理 URI
  • 教学设计师 URI
  • 教育数据分析师 URI

DevOps、量化开发复用软件开发者 URI 的项目代理关系已在 V36 注释中明确,和当前批准清单一致。

上一轮 Finding 关闭状态

第一轮/第二轮 Finding 状态 证据
C-1:混合来源未经批准 关闭 契约、design、source manifest 均已明确 ESCO 主来源 + O*NET software_skills 补充;V35/V36 字段语义与修订契约一致。
I-1:代理 SOC/ESCO URI 不可追溯 关闭 10 个职位均有 ESCO source version 和 URI;8 个唯一 URI 独立返回 200;代理 SOC、Element ID、proxy 均落表。
I-2:manifest 分类遗漏 关闭 68/68 分类存在,每个分类至少 1 条关系,清单与 V36 一致。
I-3a:Importance 逐条可追溯数据 关闭 V35 增加来源字段;V36 的 137/137 非空关系均匹配官方快照,当前数据真实。
I-3b:测试全量覆盖 47/47 软件来源 部分关闭 47 条都做集合成员验证,但只有 5 条做 skillCode -> sourceCode 精确映射断言;未断言的两个技能交换来源仍可通过。
I-3c:测试全量覆盖 137/137 非空 Importance 部分关闭 137 条都验证来源键和数值;但 proxy 只对 10 条精确断言,其余 127 条可在 1 和 0 之间翻转而不失败。

Findings

Important-1:47 条 O*NET 软件来源未做完整的逐技能精确映射断言

位置:

  • mydreamjob-server/src/test/java/com/mydreamjob/skill/SkillCloudSeedDataIntegrationTest.java:160-183
  • 精确映射单点断言:185-189
  • 辅助断言:474-478

问题:

当前循环对每条 DB source_code 只检查其三元组是否存在于快照中,再要求 DB 行数与快照行数相等。它验证了“集合完整”,但没有验证每个技能代码必须绑定哪个三元组。

可证伪反例:

  • 任意交换两个未被 assertOnetSkillSource 覆盖的 O*NET 技能来源,例如 SPRING_BOOT 与 DJANGO。
  • DB 仍然有 47 个唯一且合法的三元组,集合仍与快照相等。
  • 现有 5 条单点断言不涉及这两个技能,因此测试仍通过,但技能溯源已经被错误交换。

影响:

skills.source_code 是来源追踪字段。来源交换会静默破坏“技能到上游来源 ID”的准确性,当前回归测试无法阻止。

修复要求:

  • 在快照中增加 skill_code(仅 SOFTWARE 行使用),或新增完整的 skillCode -> SOC|Workplace Example|Element ID 映射资源。
  • 对 47/47 条 O*NET 技能逐条断言精确来源,而不是仅做集合成员检查。

Important-2:Importance Proxy 标记未做全量语义断言

位置:

  • 枚举检查:SkillCloudSeedDataIntegrationTest.java:201-203
  • 循环内代理检查:229-230
  • 仅 10 条精确断言:271-294
  • 精确断言辅助方法:481-494

问题:

assertTrue(proxy == 0 || proxy == 1) 只证明值处于合法范围,不能证明它符合种子数据的来源语义。V36 当前明确要求 137 条非空 Importance 全部是跨来源代理,必须为 1;测试只对 10 条做 proxy=1 断言。

可证伪反例:

  • 将任意一个未被 assertImportance 覆盖的非空关系从 importance_is_proxy=1 改为 0。
  • 该行仍满足数据库 CHECK 和测试的 0/1 判断,SQL 计数也不检查非空代理必须为 1,因此测试仍通过。
  • importance、SOC、Element ID 和 Data Value 均未发生变化,但来源语义已经被篡改。

影响:

代理标记是来源审计字段。当前测试不能保证 137 条全部保持已批准的 proxy 语义,无法满足“篡改 Proxy 标记必须失败”的要求。

修复要求:

  • 明确断言所有非空 Importance 的 importance_is_proxy=1,以及所有空 Importance 的 importance_is_proxy=0;或为 137 条关系提供逐关系期望映射。
  • 若业务允许直接 O*NET Importance 与代理 Importance 并存,需要按关系保存期望值并逐条断言。

Minor-1:快照的官方来源属性依赖评审时外部核验,未被自动化锁定

loadEvidence 只验证快照头部、列数、允许的 source_file 名称和键唯一性,不会读取官方 CSV 或校验官方发布包哈希。

当前快照已由本次复审用官方 ZIP 哈希确认,因此不阻断;但未来若快照的 source_file 被改成同一允许集合内的另一个文件,只要测试资源本身同步修改,现有测试无法识别来源文件标签变化。建议在来源文档或测试资源旁记录官方 ZIP/CSV 哈希,作为后续审计基线。

未决问题

  • T3 当前数据层已通过,不能因为数据正确就忽略 Important-1 和 Important-2 的回归缺口。
  • 真实 MySQL 8.4 尚未执行;本轮仅按要求执行 H2 MODE=MySQL。该限制沿用前轮,不构成本次新的阻断项。
  • V35/V36 当前满足修订后的混合来源契约;未发现需要再次 Rewind 的契约冲突。

阻断项

T3 仍为 FAIL。在补齐 47/47 逐技能精确来源映射和 137/137 Importance 代理语义断言前,不应进入 T4。当前测试 9/9 通过只能证明现有数据与当前快照一致,不能证明上述两类篡改必然失败。