From 67d83863f1d608e770ce5b56480ea4b8693e2fb5 Mon Sep 17 00:00:00 2001 From: pwklppg <123456@.com> Date: Tue, 15 Sep 2026 16:40:14 +0800 Subject: [PATCH] docs: publish skill cloud specs and release audit --- changes/n03-skill-cloud/.spec-superflow.yaml | 8 +- .../.superpowers/sdd/execution-plan.yaml | 6 +- .../n03-skill-cloud/decision-point-audit.md | 55 ++++++++++ specs/learning-path/spec.md | 88 +++++++++++++++ specs/skill-cloud/spec.md | 102 ++++++++++++++++++ specs/skill-match/spec.md | 102 ++++++++++++++++++ 6 files changed, 355 insertions(+), 6 deletions(-) create mode 100644 changes/n03-skill-cloud/decision-point-audit.md create mode 100644 specs/learning-path/spec.md create mode 100644 specs/skill-cloud/spec.md create mode 100644 specs/skill-match/spec.md diff --git a/changes/n03-skill-cloud/.spec-superflow.yaml b/changes/n03-skill-cloud/.spec-superflow.yaml index 0ef0613..37096d7 100644 --- a/changes/n03-skill-cloud/.spec-superflow.yaml +++ b/changes/n03-skill-cloud/.spec-superflow.yaml @@ -31,9 +31,11 @@ batches_completed: 3 dp_5_result: adjudicated: approved one focused repair for exact per-skill source and full proxy assertions dp_5_timestamp: 2026-09-14T09:21:01Z execution_paused: false -verification_blocked: true -verification_blocker: full mvn test has 2 failures + 9 errors in unrelated CareerProgress/JobReview/LocalResume tests -closure_requires: user-approved waiver or authorized repair +verification_blocked: false +verification_outcome: conditional-pass-with-formal-waiver +dp_6_result: pass: 74 Skill Cloud tests, OpenAPI 129 paths, package and startup fix verified; 3 unrelated pre-existing full-suite failures formally waived by user +dp_6_timestamp: 2026-09-15T08:40:05Z +test_result: pass: scoped verification with formal waiver paused_at: 2026-09-14T09:44:45Z resumed_at: 2026-09-15T01:18:50Z resume_count: 1 diff --git a/changes/n03-skill-cloud/.superpowers/sdd/execution-plan.yaml b/changes/n03-skill-cloud/.superpowers/sdd/execution-plan.yaml index 50dfe1d..0495356 100644 --- a/changes/n03-skill-cloud/.superpowers/sdd/execution-plan.yaml +++ b/changes/n03-skill-cloud/.superpowers/sdd/execution-plan.yaml @@ -24,6 +24,6 @@ waves: depends_on: [W2] state: passed_with_external_blocker receipt: pass (W3 final review; 73 targeted tests, OpenAPI 129 paths) -verification_blocked: true -verification_blocker: full mvn test has 2 failures + 9 errors in unrelated CareerProgress/JobReview/LocalResume tests -closure_requires: user-approved waiver or authorized repair of unrelated failures \ No newline at end of file +verification_blocked: false +verification_outcome: conditional-pass-with-formal-waiver +closure_requires: DP-7 confirmation pending \ No newline at end of file diff --git a/changes/n03-skill-cloud/decision-point-audit.md b/changes/n03-skill-cloud/decision-point-audit.md new file mode 100644 index 0000000..ebc7f93 --- /dev/null +++ b/changes/n03-skill-cloud/decision-point-audit.md @@ -0,0 +1,55 @@ +# N03 Decision Point Audit + +审计时间:2026-09-15T08:40:05Z + +## Decision Point Status + +| DP | Status | Evidence | +| --- | --- | --- | +| DP-0 | confirmed | 需求、范围、非目标、语言与 Full workflow 已确认 | +| DP-1 | confirmed | 三接口需求口径已确认 | +| DP-2 | approved | proposal、三份 delta specs、design、tasks 已审阅 | +| DP-3 | approved | execution contract revision 2 已批准 | +| DP-4 | approved | SDD;W1/W2/W3 串行 wave,依赖关系已固化 | +| DP-5 | adjudicated | T3 来源与可证伪性修复获人工批准 | +| DP-6 | pass with formal waiver | 功能范围内验证通过;全量测试中的既有无关失败由用户正式豁免 | +| DP-7 | pending | 等待归档确认 | + +## Contract Coverage + +- W1:来源清单、V35 词库结构、V36 代表性种子数据、可追溯 O*NET provenance。 +- W2:Repository、词云排序、个人匹配、学习路径,50 项 wave 测试通过。 +- W3:三个 STUDENT 接口、真实权限测试、端到端一致性、接口文档和 OpenAPI。 +- 14 条 MUST/SHALL 行为均有实现与测试证据。 +- delta specs 已发布到根 baseline:specs/skill-cloud、specs/skill-match、specs/learning-path,共 14 条 Requirements,无冲突。 + +## Verification Evidence + +- Skill Cloud 指定测试组:74 tests,0 failures,0 errors,BUILD SUCCESS。 +- mvn -DskipTests package:BUILD SUCCESS。 +- python docs\verify_openapi.py:paths 129,All checks passed。 +- 启动缺陷修复:V37 将 importance_is_proxy 规范化为跨方言 BOOLEAN;H2 MODE=MySQL 与 MySQL 8.0.43 冒烟通过。 +- 完整 mvn test:1396 tests,2 failures,1 error,BUILD FAILURE。 +- 本次完整测试失败: + - CareerProgressServiceTest.fullAggregation_returnsCorrectSummaries:既有时间依赖。 + - JobReviewDimensionControllerTest.listReviews_shouldReturnDimensionScores:既有 Mock 签名漂移。 + - JobReviewDimensionServiceTest.listReviews_shouldReturnDimensionScoresWithoutN1:既有 Repository 方法签名漂移。 +- 这些失败位于本变更未修改的模块;Skill Cloud 测试在全量套件中全部通过。 + +## Formal Waiver + +用户于 2026-09-15 明确批准:将与本功能无关的既有失败作为正式豁免记录,并进入 release-archivist。当前 fresh run 只剩上述 3 条历史阻塞;更早一轮曾包含 LocalResumeStorageServiceTest 的 Windows 临时目录错误,后续环境运行已恢复。本豁免覆盖所有与本功能无关、且在变更前后可独立复现的既有失败。 + +## Scope and Coherence + +- 变更实现集中在 com.mydreamjob.skill、Flyway V35–V37、SecurityConfig、docs、OpenAPI 与对应测试。 +- 未修改 V1–V34 历史迁移或已有接口语义。 +- V37 是启动兼容修复,不改变 API 行为或业务公式。 +- W2/W3 独立评审均已通过;运行时启动修复也已独立复审 PASS。 +- 已知非阻断项:heatSource 设计表述与实际 heatSourceCode/heatElementId/heatProxy 字段命名不同;文档已按实际字段发布。 + +## Remaining Actions + +1. 记录 DP-7 归档确认。 +2. 将状态从 executing 转为 closing。 +3. ssf CLI 不可用;物理归档/合并需在获得用户确认后手工执行,且必须排除仓库中已有的无关未提交文件。 \ No newline at end of file diff --git a/specs/learning-path/spec.md b/specs/learning-path/spec.md new file mode 100644 index 0000000..613d2ca --- /dev/null +++ b/specs/learning-path/spec.md @@ -0,0 +1,88 @@ +# learning path Specification + +## Purpose + +定义基于缺失 TOP10 技能的确定性学习路径生成行为。 + +## Requirements + +### Requirement: Learning path consumes unmatched TOP10 skills + +学习路径接口 MUST 复用个人技能匹配结果,并只包含当前 TOP10 中未命中的技能;不得包含已命中技能或 TOP10 之外的技能。 + +#### Scenario: Student has skill gaps + +- **WHEN** 当前 TOP10 中部分技能未命中 +- **THEN** 学习路径 MUST 包含全部未命中技能且不含已命中技能 + +#### Scenario: Student has no skill gaps + +- **WHEN** 当前 TOP10 全部命中 +- **THEN** 学习路径 MUST 返回空列表和总数 0 + +#### Scenario: Student has no matching skill evidence + +- **WHEN** 学生没有技能数据 +- **THEN** 学习路径 MUST 包含当前 TOP10 的全部技能 + +### Requirement: Learning priority follows heat order + +学习路径项 MUST 按与 TOP10 相同的热度顺序排列,并从 1 开始连续编号;空热度技能继续遵守词云的末位排序规则。 + +#### Scenario: Multiple missing skills are ordered + +- **WHEN** 缺失技能包含多个热度不同的技能 +- **THEN** 热度最高的技能 MUST 获得优先级 1,后续技能依次编号 + +#### Scenario: Missing skills have equal heat + +- **WHEN** 缺失技能热度相同 +- **THEN** 优先级 MUST 沿用词云的稳定次级排序,重复请求结果一致 + +#### Scenario: Only one skill is missing + +- **WHEN** 当前 TOP10 只有一项未命中 +- **THEN** 该技能 MUST 返回优先级 1 + +### Requirement: Learning suggestions are deterministic and industry-neutral + +每项学习建议 MUST 由后端按技能分类的通用模板生成,不调用 AI、知识库问答或外部课程服务;建议不得包含课程链接、讲师、价格、学习时长或完成度。 + +#### Scenario: Programming language gap is returned + +- **WHEN** 缺失技能属于编程语言分类 +- **THEN** 建议 MUST 描述基础语法学习和编码练习方向 + +#### Scenario: Framework gap is returned + +- **WHEN** 缺失技能属于框架分类 +- **THEN** 建议 MUST 描述核心概念学习和小型项目实践方向 + +#### Scenario: Tool gap is returned + +- **WHEN** 缺失技能属于工具分类 +- **THEN** 建议 MUST 描述安装配置和工程实践方向 + +#### Scenario: Unknown category is returned + +- **WHEN** 缺失技能无法匹配特定模板分类 +- **THEN** 系统 MUST 返回一条行业无关的通用学习建议 + +### Requirement: Learning path response is actionable and minimal + +每个学习路径项 MUST 返回技能 ID、技能名称、技能分类、优先级和简化学习建议;响应 MUST 不包含个人隐私、简历原文或模型推理内容。 + +#### Scenario: A learning item is generated + +- **WHEN** 存在未命中技能 +- **THEN** 每个路径项 MUST 同时具备技能身份、分类、连续优先级和建议文本 + +#### Scenario: Repeated request is made + +- **WHEN** 学生资料、词库数据和筛选条件均未变化 +- **THEN** 接口 MUST 返回相同顺序、相同优先级和相同建议 + +#### Scenario: Response contains no private profile data + +- **WHEN** 学习路径成功返回 +- **THEN** 响应 MUST 不含手机号、邮箱、简历文件、原始 `skills_json` 或其他无关个人数据 diff --git a/specs/skill-cloud/spec.md b/specs/skill-cloud/spec.md new file mode 100644 index 0000000..5201287 --- /dev/null +++ b/specs/skill-cloud/spec.md @@ -0,0 +1,102 @@ +# skill cloud Specification + +## Purpose + +定义行业技能词云查询、筛选、热度排序与来源追溯行为。 + +## Requirements + +### Requirement: STUDENT-only skill cloud access + +系统 MUST 只允许已登录 STUDENT 调用技能词云接口,并从 JWT 解析当前用户,不接受客户端传入学生身份参数。 + +#### Scenario: Anonymous request is rejected + +- **WHEN** 未携带有效 JWT 请求 `GET /api/v1/skills/word-cloud` +- **THEN** 系统 MUST 返回 401,且不得返回任何词库数据 + +#### Scenario: Company or administrator request is rejected + +- **WHEN** COMPANY 或 ADMIN 用户携带有效 JWT 请求技能词云 +- **THEN** 系统 MUST 返回 403,且不得返回任何词库数据 + +#### Scenario: Student request is accepted + +- **WHEN** STUDENT 用户携带有效 JWT 请求技能词云 +- **THEN** 系统 MUST 按筛选条件返回词库数据 + +### Requirement: Skill cloud filter validation + +接口 MUST 要求 `industryId` 和 `occupationId` 为必填正整数,`categoryId` 为可选正整数;行业与职位必须存在且互为关联,技能分类存在时必须属于该职位。 + +#### Scenario: Required filters are missing + +- **WHEN** 请求未传 `industryId` 或 `occupationId` +- **THEN** 系统 MUST 返回 400,并说明缺失的筛选参数 + +#### Scenario: Occupation does not belong to industry + +- **WHEN** 请求传入存在但不属于目标行业的职位 +- **THEN** 系统 MUST 返回 400,且不得返回技能数据 + +#### Scenario: Category does not belong to occupation + +- **WHEN** 请求传入存在但不属于目标职位的技能分类 +- **THEN** 系统 MUST 返回 400,且不得返回技能数据 + +#### Scenario: Category is omitted + +- **WHEN** 请求只传行业和职位 +- **THEN** 系统 MUST 返回该职位下所有技能分类的技能 + +### Requirement: Complete ranked skill cloud + +系统 MUST 返回当前筛选范围内全部技能,并按 `heat` 降序排列;`heat` 为空时排在非空值之后。同 `heat` 或同为空值时,系统 MUST 使用稳定的中文技能名称和技能 ID 作为次级排序条件。 + +#### Scenario: Skills are ordered by heat + +- **WHEN** 筛选结果存在多个带 O*NET Importance 的技能 +- **THEN** 返回列表 MUST 按数值从高到低排列 + +#### Scenario: Skills without heat are retained + +- **WHEN** 筛选结果包含无 O*NET Importance 的 ESCO 技能 +- **THEN** 这些技能 MUST 保留在列表中,`heat` 为 null,并排在所有非 null 热度技能之后 + +#### Scenario: Equal heat has deterministic order + +- **WHEN** 两个技能具有相同 `heat` +- **THEN** 系统 MUST 按稳定次级规则返回一致顺序,重复请求不得随机变化 + +#### Scenario: Empty scope returns empty list + +- **WHEN** 合法筛选范围内没有技能 +- **THEN** 系统 MUST 返回成功响应、空技能列表和总数 0 + +### Requirement: TOP10 is derived from the skill cloud + +系统 MUST NOT 为 TOP10 维护独立排序或独立数据源;前端必须能够直接使用词云返回列表的前十条得到 TOP10。 + +#### Scenario: Scope has at least ten skills + +- **WHEN** 词云结果包含至少十条技能 +- **THEN** 前十条 MUST 与同一筛选条件下的热门技能 TOP10 一致 + +#### Scenario: Scope has fewer than ten skills + +- **WHEN** 词云结果少于十条技能 +- **THEN** 全部结果 MUST 构成实际 TOP10 集合 + +### Requirement: Skill cloud response exposes source-linked metadata + +每个技能项 MUST 至少返回技能 ID、中文名称、技能分类 ID、技能分类名称、`heat` 和稳定排名。技能仍可通过 ID 追溯至 ESCO/O*NET 来源。 + +#### Scenario: Skill item is returned + +- **WHEN** 查询返回任一技能 +- **THEN** 响应 MUST 包含该技能的身份、分类、热度和排名字段 + +#### Scenario: Response is wrapped consistently + +- **WHEN** 请求成功 +- **THEN** 响应 MUST 使用项目统一 `ApiResponse` 结构,并在 `data` 中返回筛选条件和技能列表 diff --git a/specs/skill-match/spec.md b/specs/skill-match/spec.md new file mode 100644 index 0000000..823208d --- /dev/null +++ b/specs/skill-match/spec.md @@ -0,0 +1,102 @@ +# skill match Specification + +## Purpose + +定义个人技能与当前热门 TOP10 的确定性匹配行为。 + +## Requirements + +### Requirement: Personal matching uses the current ranked TOP10 + +个人技能匹配接口 MUST 复用技能词云相同的筛选参数、数据范围和热度排序;匹配集合 MUST 是当前筛选结果的前 `min(10, 实际技能数)` 项。 + +#### Scenario: Filtered result has at least ten skills + +- **WHEN** 当前筛选返回十条或更多技能 +- **THEN** 匹配集合 MUST 恰好包含热度最高的十条技能 + +#### Scenario: Filtered result has fewer than ten skills + +- **WHEN** 当前筛选返回六条技能 +- **THEN** 匹配集合 MUST 包含全部六条技能,分母 MUST 为 6 + +#### Scenario: Filtered result is empty + +- **WHEN** 当前合法筛选范围内没有技能 +- **THEN** 匹配度 MUST 返回 0,命中和缺失列表 MUST 均为空 + +### Requirement: Student skills are read from the authenticated profile + +系统 MUST 从当前登录 STUDENT 的 `student_profiles.skills_json` 读取技能;不得接受客户端另行提交的技能列表作为个人匹配依据。 + +#### Scenario: Student profile has valid skills + +- **WHEN** 当前学生存在合法技能数组 +- **THEN** 系统 MUST 使用该数组计算匹配结果 + +#### Scenario: Student profile is absent or has no skills + +- **WHEN** 当前学生没有资料记录、技能字段为空或技能数组为空 +- **THEN** 匹配度 MUST 返回 0,命中列表为空,缺失列表为当前 TOP10 + +#### Scenario: Skills JSON is malformed + +- **WHEN** 当前学生技能字段无法解析为字符串数组 +- **THEN** 系统 MUST 按无技能处理,不得导致 500 响应或泄露原始字段内容 + +### Requirement: Skill matching normalizes aliases + +系统 MUST 使用技能标准和别名进行匹配,归一化至少覆盖首尾空白、大小写、连续空格和常见标点差异;每个标准技能命中一次即可。 + +#### Scenario: Alias matches canonical skill + +- **WHEN** 学生技能为 `SpringBoot`,TOP10 标准技能为 `Spring Boot` 且存在对应别名 +- **THEN** 系统 MUST 将该技能计为命中 + +#### Scenario: Case and spacing differ + +- **WHEN** 学生技能与标准技能的字母大小写或空格数量不同 +- **THEN** 系统 MUST 在归一化后判断为同一技能 + +#### Scenario: Duplicate student skills exist + +- **WHEN** 学生技能数组包含多个指向同一标准技能的别名 +- **THEN** 系统 MUST 只计一次命中 + +### Requirement: Match score uses the actual TOP10 size + +匹配度 MUST 使用公式 `round(命中数 × 100 / min(10, 实际技能数))` 计算,并返回 0 到 100 的整数。实际技能数为 0 时,匹配度 MUST 为 0。 + +#### Scenario: All skills match + +- **WHEN** 当前 TOP10 有 8 项,学生命中全部 8 项 +- **THEN** 匹配度 MUST 返回 100 + +#### Scenario: Partial match is rounded + +- **WHEN** 当前 TOP10 有 3 项,学生命中 1 项 +- **THEN** 匹配度 MUST 返回 33 + +#### Scenario: No skill matches + +- **WHEN** 当前 TOP10 非空且学生没有命中任何技能 +- **THEN** 匹配度 MUST 返回 0 + +### Requirement: Match response separates matched and missing skills + +响应 MUST 返回匹配度、命中数量、实际匹配集合大小、命中技能列表和缺失技能列表;缺失技能 MUST 保持 TOP10 的热度排序。 + +#### Scenario: Some skills are matched + +- **WHEN** 学生命中 TOP10 的部分技能 +- **THEN** 命中列表和缺失列表 MUST 分别返回对应技能,且并集等于 TOP10 + +#### Scenario: All skills are matched + +- **WHEN** 学生命中 TOP10 的全部技能 +- **THEN** 缺失技能列表 MUST 为空,匹配度 MUST 为 100 + +#### Scenario: No skills are matched + +- **WHEN** 学生没有命中 TOP10 的任何技能 +- **THEN** 命中技能列表 MUST 为空,缺失列表 MUST 等于 TOP10