jackwener/group-workshop · Archived

grading-backend

Grade a team's backend implementation against its spec on 9 weighted dimensions, producing a markdown report + JSON summary with evidence.

First seen Apr 15, 2026

Installation

$ npx skills add jackwener/group-workshop --skill grading-backend

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from jackwener/group-workshop.

npx skills add jackwener/group-workshop

Browse all from jackwener/group-workshop

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Declared
Cursor Declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 1
Default branch main
Open issues 0
Status Archived

Skill metadata

Parsed from SKILL.md frontmatter.

Declared agents claude-code cursor

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 33,463 B
  • docs SUMMARY.md 161 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

grading-backend

给 AI 培训项目后端实现打分。50 学员分成 ~12 组,按 spec-driven 方式实现本地部署的后端服务。本 skill 让 Claude(或 Qoder / Cursor / Claude Code agent)按统一 rubric 给某一组的后端打分,产出可给学员的 markdown 报告 + 可汇总 leaderboard 的 JSON。


前置依赖

  • 本地可跑:待评后端能在本机启动(npm run dev / uvicorn ... / go run ./... / ./gradlew bootRun 等),默认 http://localhost:8080(按项目 README 调整),有 health check 端点(/health / /healthz / /ping)或其他就绪信号。
  • Spec 文档位置<project>/docs/spec.md<project>/README.md<project>/spec/*。找不到则先向用户确认 spec 路径再开工,不要凭空评分。
  • Node.js 环境:Node ≥ 18,用来跑 npx ajv-cli 校验 JSON、npx autocannon 做压测。
  • curl:所有接口探针和取证命令基于 curl,macOS / Linux 自带即可。
  • 输出目录:所有证据和报告写到待评项目根目录下的 .grading/(已由 spec 约定 gitignore)。

约定的工作目录:所有 probe 脚本和 .grading/ 输出都默认在待评项目根目录下执行。即评分时:

  1. cd 到被评项目(如 cd group-workshop/group-2
  2. 用绝对路径调用 probe:bash $WORKSHOP_ROOT/skills/grading-backend/probes/probe-robustness.sh team-2 http://localhost:8080 /api/items
  3. .grading/ 会落到被评项目根下,与该项目共生

共享契约(必读)

评分开始前,必须先读完本文件 ## 评分标尺 / ## 报告模板 / ## 证据硬约束 三段,以及 ../score-schema.json。这些是 frontend / backend 两个 skill 共用的硬约束,不读会导致输出格式不一致、无法汇总 leaderboard:

  1. 见本文件 ## 评分标尺 — 0–10 标尺含义 + S/A/B/C/D 档位定义。所有维度打分必须对照此标尺
  2. 见本文件 ## 报告模板 — 统一 Markdown 报告模板。产出的 report.md 必须照此结构填充。
  3. ../score-schema.json — JSON 摘要的 schema。产出的 summary.json 必须通过此 schema 校验。
  4. 见本文件 ## 证据硬约束 — 打分 ≥7 或 ≤4 必须 ≥2 条证据,5–6 分必须 ≥1 条。违反则打分视为无效。

评分标尺(0–10)

本段是 grading-frontendgrading-backend 两个 skill 共享的唯一打分锚点。两侧所有维度得分都必须按本表落点,再用证据说明为什么不是相邻的锚点。

1. 分数锚点

分数 名称 判定标准
10 工业级范本 可直接作为下一届教学样例;评分人找不到该维度的改进空间;相对已见过的最好学员作业也明显领先
9 优秀 核心完全到位,仅 1–2 处非关键瑕疵(例如边缘场景文案、极端断点、冷路径日志)
7 良好 主流程到位,但存在 ≥3 处可改进;不影响交付但明显看得出赶工痕迹
5 及格 Happy path 能跑,但明显粗糙;有 1 类系统性漏洞(如缺状态、缺校验)但未致命
3 不及格 该项存在尝试痕迹但严重不完整;主要子项缺失或逻辑错误
0 未实现 完全缺失,或实现了但跑不起来
N/A 不适用 spec 明确未要求,或该维度在本项目栈上无意义;必须写原因,不计入总分

允许的分数:0、3、5、7、9、10 是锚点;1/2/4/6/8 仅在证据支持"介于两锚点之间"时使用。不允许出现小数。

2. 打分操作规则

  1. 先选锚点,再微调:拿到证据后先对齐最接近的锚点(10/9/7/5/3/0),再用证据决定 ±1 微调。禁止"先拍个 8 再补证据"。
  2. 高低分必须有证据

- score ≥ 7 → 至少 2 条证据 - score ≤ 4 → 至少 2 条证据 - 5–6 → 至少 1 条证据 - 详细规则见本文件 ## 证据硬约束

  1. N/A 必须写原因na_reason 字段必填,格式 "spec §X.Y 未涉及此项""技术栈 Z 无此概念"。N/A 不计入总分。
  2. 不能同时低分 + 高评:如果评语是"很好,只差一点",分数不能 ≤ 5。
  3. 锚点冲突时就低不就高:若一个维度既符合 9 的正面描述也命中 5 的负面描述,取 5(警戒作用)。

3. 总分计算

公式(N/A 不计入分母):

total = Σ(score_i × weight_i) / Σ(weight_i for dim_i where score_i != null) × 100 / 10

等价形式:先对非 N/A 维度算加权平均(0–10),再把有效权重归一化到 100,最后乘 10 映射到 0–100。

例子:前端 10 维总权重 100,若 微文案 (权重 6) 被判 N/A:

  • 有效权重总和 = 94
  • 其余 9 维加权和 = Σ(scorei × weighti)
  • total = (Σ / 94) × 10

4. 总分档位

档位 分数区间 含义
S ≥ 90 可作为本届教学样例
A 80–89 合格交付,小范围打磨即可上线
B 70–79 主流程 OK,系统性问题需要返工
C 60–69 能 demo,不能交付;多处结构性缺陷
D < 60 未达培训目标,需重做关键模块

档位只用于班级 leaderboard 横向对比,学员反馈以维度得分 + 证据 + 改进清单为主。


报告模板

两个 grading skill 产出的 .grading/reports/<team>-<side>.md 必须严格按本模板。字段缺一不可;无信息时填 不要省略行。模板本体用四个反引号包裹的 Markdown 代码块呈现,复制时去掉最外层反引号。

使用说明

  • 占位符形如 {队伍名},落地时替换为实际值
  • 维度详评块按 grading-frontend 10 维 / grading-backend 9 维完整展开,不能合并或省略
  • 任一维度证据不足时,score 填 nullnareasonEVIDENCEMISSING: ...,并在末尾"评分风险"段汇总
  • 报告的同级必须存在对应 JSON:<team>-<side>.json,schema 见 ../score-schema.json

模板

````markdown

项目评分报告 — {队伍名}({前端 | 后端})

  • 评分人:{Claude@model-id / 助教姓名}
  • 日期:{YYYY-MM-DD}
  • Spec 版本:{git sha 或 docs/spec.md 路径}
  • 被评项目 commit:{sha}
  • 评分耗时:{xx 分钟}

总分:{score} / 100 — 等级 {S | A | B | C | D}

维度汇总

# 维度 得分 权重 加权 一句话
1 {维度名} {0–10 或 N/A} {w} {score×w/10} {一句话概述}
2 ... ... ... ... ...

有效权重总和:{Σw_non-NA};N/A 维度:{列表或 "无"}

逐维度详评

1. {维度名} — {score}/10(权重 {w})

证据

  • path/to/file.ext:L12-L40 — {这条证据说明了什么}
  • 截图:.grading/shots/{team}-{view}.png
  • 命令:curl -s localhost:8080/api/x.grading/probes/{team}-x.log

要到 10 差什么

  1. {具体、可操作的改进建议}
  2. {...}
  3. {...}

2. {维度名} — {score}/10(权重 {w})

(同样模板,完整列出所有维度)


亮点

  • {值得表扬的 2–4 条,要带证据}

最该优先修的 3 件事

  1. {问题} — 影响:{哪个维度掉了多少分};改法:{一两句话说清}
  2. ...
  3. ...

评分风险(可选)

仅当出现以下情况时填写,否则删除整节:

  • 证据不足被判 EVIDENCE_MISSING 的维度列表
  • 评分人对某维度存疑但无法进一步取证的原因
  • 项目未跑起来 / spec 缺失等影响评分可信度的事实

````

校验清单(写完报告后自检)

  • 总分 = Σ(加权列) × 10 / Σ(有效权重),数值对得上
  • 每个维度都有至少一条引用(证据 / 截图 / 日志 / spec 条目)
  • 所有 N/A 都有 na_reason
  • "最该优先修的 3 件事" 每条都回指了具体维度
  • 对应 JSON 已写入 .grading/reports/<team>-<side>.json

证据硬约束

本段是打分是否有效的判定规则。grading-frontendgrading-backend 两个 skill 在写入每个维度分数前必须先检查本表;证据不达标的打分视为无效。

设计目的:防止 agent 在缺乏实际观察的情况下拍脑袋打分。宁可多一个 EVIDENCE_MISSING,不要编一个好看的分数

1. 证据条数规则

分数段 最少证据条数 说明
9–10 3 高分必须可复现;至少覆盖:1 条代码引用 + 1 条运行期证据(截图/probe/log) + 1 条 spec 比对
7–8 2 需同时体现"主流程到位"与"非关键瑕疵"
5–6 1 一条即可,但必须能指出具体缺陷
3–4 2 低分不能靠印象;必须列出 ≥2 个具体失败点
0 1 至少证明"真的没有":grep 结果、spec 条目 + 代码库缺失、启动失败日志
N/A 1 必须引用 spec 条目或技术栈说明,证明该项"不适用"

2. 证据合法形式

每条证据必须是以下 4 类之一,不得为自然语言转述:

  1. 代码引用path/to/file.ext:L{start}-L{end}path/to/file.ext:L{line}

- 路径必须相对项目根 - 必须真实存在(评分前 open / grep 确认过)

  1. 截图.grading/shots/<team>-<view>.png

- 文件必须存在;后端维度一般不使用 - 可附简短说明:"登录页 desktop 1280×800,有 layout shift"

  1. 命令日志.grading/probes/<team>-<probe>.log 或在证据行内直接给出命令与关键输出

- 形如:curl -s -o /dev/null -w "%{http_code}" localhost:8080/api/x -d '{}' → 500 - 压测结果引用 autocannon 输出文件

  1. Spec 引用spec §3.2 / docs/spec.md:L120-L145 / issue 链接

- 判定 N/A 或 Spec 一致性时必用

3. 违规处理

3.1 证据不足

若按 §1 无法凑够所需证据条数:

  1. score 置为 null
  2. nareason 写为 "EVIDENCEMISSING: 需要 N 条证据,仅能取得 M 条(已尝试:<动作清单>)"
  3. 在报告末尾 ## 评分风险 段落列出该维度
  4. 该维度不计入总分分母(与正常 N/A 一致),但在评分风险段标注"非 spec 排除项"

3.2 证据伪造 / 占位

评分人/agent 若写入以下任一形式的证据,该维度直接作废并降级为 EVIDENCE_MISSING

  • 路径不存在的 file:line(lint:评分后用 test -f 批量校验)
  • 截图文件不存在或大小为 0
  • 命令日志内容与结论自相矛盾
  • 描述性语言("代码组织得不错")未附具体引用

3.3 高低分无证据

  • score ≥ 7 但证据 < 2 条 → 降为 6,或标 EVIDENCE_MISSING
  • score ≤ 4 但证据 < 2 条 → 升为 5,或标 EVIDENCE_MISSING

选择哪个处理取决于评分人对维度的把握;两种方式都必须在"评分风险"中注明。

4. 证据收集建议顺序

先做便宜的、可批量的,再做昂贵的:

  1. 静态 (cheap):grep / rg 扫关键词、看 package.json / schema / migration
  2. Spec 对齐:逐条把 spec 检查项对到代码文件
  3. 运行期:启 dev server / 后端服务,跑截图脚本 / probe 脚本
  4. 交互:走完主流程,看 console / network / 日志
  5. 测试:跑 test suite,看覆盖率与失败清单

每跑完一步把产物落盘到 .grading/,在报告证据行里引用相对路径即可。


配套资源

按需读取(不是每次都读全部):

  • ./anti-patterns.md — 后端反模式清单。打"健壮性"、"代码分层"、"性能"三个维度时必读,用来识别 controller 里写 SQL、裸 try/except: pass 吞异常、循环里 query 造成 N+1、写操作非幂等、错误响应结构每接口不同等典型滑坡。
  • ./examples/good-report.md./examples/mediocre-report.md — 两份标定样例报告。打分前读一次做分布校准,避免全班都打 8 分这种分不开档的问题。
  • ./probes/probe-robustness.sh — 健壮性探针组合,跑 8 个场景(空 body / 错类型 / 超长字段 / 缺必填 / 越权 / 幂等重放 / 并发冲突 / 未登录)。取证时直接 bash ./probes/probe-robustness.sh <team> <base-url> <path>,不要手搓 curl。每个 probe 写 .grading/probes/<team>-<probe-name>.log,总表写 .grading/probes/<team>-robustness-summary.md
  • ./probes/probe-performance.sh — 基于 npx autocannon 的 p50/p95/p99 压测脚本,输出 .grading/probes/<team>-autocannon.log(原始 autocannon 输出)和 .grading/probes/<team>-perf-summary.txt(解析后的 p50/p95/rps + rubric 建议)。

评分流程(5 步)

步骤 1:定位与启动

  1. 确认 spec 路径,记录 spec_sha(若 spec 在 git 仓库内)。
  2. 读完 spec,列出:接口清单(method + path + 入参 + 出参 + 状态码) / 数据模型 / 权限角色 / 关键写操作。
  3. package.json / pyproject.toml / go.mod / pom.xml 识别技术栈(Node/Python/Go/Java、ORM、测试框架、日志库),记录 project_sha
  4. 启动后端服务,确认 http://localhost:<port>/<health> 可访问(curl 拿到 200 或 spec 约定的就绪响应)。如启动失败直接记录在报告"启动阻塞"一节,本维度不硬给 0 分,但在"Spec 一致性"里扣分。

步骤 2:收集证据(evidence-first)

静态证据

  • 用 Grep/Glob 扫接口清单、分层目录(controller/service/repo)、schema / 迁移、测试目录、日志代码。
  • 为每个维度预留至少 1 处 file:line 引用。

动态证据

# 健壮性:对主要写接口跑一遍 8 个 probe
bash ./probes/probe-robustness.sh <team> http://localhost:8080 /api/items

# 性能:对 GET 主列表接口 & POST 主写接口各压一次
bash ./probes/probe-performance.sh <team> http://localhost:8080 /api/items

# 测试套件(按栈择一)
npm test -- --coverage        # Node
pytest --cov                  # Python
go test -cover ./...          # Go

# 手工 curl:对 spec 列出的每个接口至少调一次,记录 method + path + 状态码 + 响应体
curl -i -X GET  http://localhost:8080/api/items
curl -i -X POST http://localhost:8080/api/items -H 'Content-Type: application/json' -d '{"name":"demo"}'

所有 probe 日志落到 .grading/probes/<team>-*.log,手工 curl 的关键输出也要 tee.grading/probes/<team>-curl.log

交互证据:按 spec 的主业务流(注册 → 登录 → 创建 → 查询 → 更新 → 删除,或 spec 指定流程)串一遍,记录每步状态码、响应体片段、异常。

步骤 3:按 9 维度逐条打分

⚠️ 取证不足时的强制行为

- 维度打分 ≥7 或 ≤4 时,证据条数低于 evidence-requirements.md 规定的最低条数 → score 必须置 null,nareason 写 "EVIDENCEMISSING: 已尝试 X、Y,未能取得 Z"
- 这与 "spec 未要求" 的 N/A 是两回事:N/A 写 "N/A — spec §X.Y 未规定"
- 所有 EVIDENCE_MISSING 维度必须在报告底部 "## 评分风险" 段汇总列出

对照本文件下方 rubric(以及本文件 ## 评分标尺 段),逐维度 0–10 打分。每一条打分必须附证据(file:line / curl log 行号 / probe log 行号 / 测试输出行号)。spec 未要求的维度写 "N/A — 原因",不计入总分。

步骤 4:产出 report.md + summary.json

按本文件 ## 报告模板 段填 .grading/reports/<team>-backend.md;按 ../score-schema.json.grading/reports/<team>-backend.json。每维度附"要到 10 差什么"2–4 条。

步骤 5:自检(必须通过)

# JSON schema 校验
npx ajv-cli validate -s skills/score-schema.json \
  -d .grading/reports/<team>-backend.json

# 证据数量自检(每维度统计 file:line + probe log + curl log 引用总数)
grep -cE "\.(ts|js|py|go|java|sql|log):" .grading/reports/<team>-backend.md

自检不过则回到步骤 2 补证据,不得放过。


9 个评分维度(权重合计 100)

# 维度 权重
1 Spec 一致性 20
2 健壮性 15
3 API 设计 12
4 测试 12
5 数据建模 10
6 代码分层 10
7 性能 8
8 可观测性 7
9 文档 6

1. Spec 一致性(权重 20)

关注点

  • spec 列出的每个接口(method + path)是否都实现了。
  • 入参字段、出参字段、状态码、错误码是否与 spec 对齐。
  • 权限 / 角色分支(如管理员接口)是否完整。
  • 既不偷工减料,也不无关超纲。

锚点

  • 10:spec 列出的所有接口 100% 实现;入参 / 出参 / 状态码 / 错误码完全对齐;超纲功能明确标注为增强。
  • 9:所有接口都实现,仅 1–2 处非关键字段或错误码与 spec 微差(如 response 里多了一个无害字段、错误码用 400 而 spec 要 422),核心路径 100% 对齐。
  • 8:主要接口 + 次要接口全部实现,spec 边角有遗漏(如 1 个非主路径查询参数未支持、1 个 soft-delete 恢复接口缺失),业务闭环完整。
  • 7:主要接口齐全,但遗漏 1–2 个次要接口或 2–3 处字段细节 / 状态码错位。
  • 5:核心 CRUD 在,但多处字段偏离 spec,或 1 个关键接口(如审核、权限切换)未实现。
  • 3:大段偏离 spec,只实现了 demo 级接口,真实业务流程缺失。

取证方法

  1. 把 spec 接口清单与代码里的路由注册逐一对应:

``bash # Node/Express 举例 grep -rnE "app\.(get|post|put|patch|delete)|router\.(get|post|put|patch|delete)" src/ # FastAPI grep -rnE "@(app|router)\.(get|post|put|patch|delete)" . # Go/Gin grep -rnE "\.(GET|POST|PUT|PATCH|DELETE)\(" . ``

  1. 对 spec 的每个接口手工 curl 一遍,记录状态码与响应体。
  2. 缺失项记录 spec 章节号 + 期望行为 + 实际行为。

证据要求

  • 至少 4 条 file:line 引用到路由 / handler 文件。
  • 一份 spec vs 实现的对齐表(报告里用 markdown 表格:接口 / spec 要求 / 实际 / 状态)。
  • 至少 3 条 curl log 覆盖关键接口。

2. 健壮性(权重 15)

关注点

  • 所有异常路径是否被处理(不是 try/except: pass 吞错)。
  • 输入是否校验(类型、范围、必填、长度)。
  • 关键写操作是否幂等(重试不重复创建)。
  • 并发冲突是否考虑(乐观锁 / 事务)。
  • 未登录 / 越权访问是否正确拒绝。

锚点(基于 probe-robustness.sh 8 个 probe 的通过数)

  • 10:≥7 个 probe 通过;有明确错误码体系;关键写操作幂等;并发路径有锁或事务。
  • 9:8 个 probe 通过 7 个,仅 1 处边界返回 500 而非结构化错误(如超长字段未截断就抛 DB error);幂等 / 并发 / 错误码体系完整。
  • 8:6/8 probe 合理(主操作幂等做了,越权 / 未登录拒绝正确),1–2 处边界未处理但返回的是统一错误结构而非堆栈。
  • 7:≥5 个 probe 通过;主路径健壮,边界 / 并发有 1–2 处疏漏。
  • 5:≥3 个 probe 通过;happy path 能跑,异常路径多处直接 500。
  • 3:<3 个 probe 通过;基本没做异常处理,或报错直接把堆栈抛给前端。

取证方法

  1. bash ./probes/probe-robustness.sh <team> <base-url> <path>,记录 8 个 probe 的通过数(每个 probe 有期望状态码)。
  2. 检查响应码和错误信息格式是否统一:

``bash grep -rnE "try\s\{|try:" src/ | head -20 grep -rnE "except\s:|catch\s\(\s\)" src/ # 裸 except / catch grep -rnE "pass\s*$|// ignore" src/ ``

  1. 对同一写接口用 curl 打两次相同 payload,看是否产生两条记录(幂等)。

证据要求

  • 1 份 <team>-robustness-summary.md(标注通过数 X/8)+ 至少 3 份 <team>-<probe-name>.log
  • 至少 3 个 probe 的 curl + 响应摘录(报告里贴片段)。
  • 至少 2 条 file:line 引用校验 / 错误处理代码。

3. API 设计(权重 12)

关注点

  • RESTful 路径语义(资源复数名词、层级清晰)或 RPC 风格的一致性。
  • 状态码用对(200/201/204/400/401/403/404/409/422/500)。
  • 错误响应结构统一(如 {code, message, details}),不是每接口一种形状。
  • 分页 / 过滤 / 排序有约定(?page=&size= 或 cursor)。
  • 请求 / 响应字段命名一致(camelCase 或 snake_case 二选一不混用)。

锚点

  • 10:路径和状态码全部合规;错误结构统一;分页 / 过滤 / 排序标准化;字段命名统一。
  • 9:路径 / 状态码 / 错误结构全部合规,分页和命名统一,仅 1 处过滤参数风格与全站不完全一致(如一个接口用 q 其他用 keyword)。
  • 8:RESTful 合规且错误结构统一,分页做了但 1 个接口返回 201 而 spec 约定 200(或反之),字段命名统一。
  • 7:整体合规,但 1–2 个状态码错位(如 POST 成功返 200 而不是 201)或 1 处错误结构不一致。
  • 5:多数接口能用,但状态码乱用、错误结构每接口一种、分页无约定。
  • 3:全部 200 + {ok: false},无状态码体系,路径随意。

取证方法

  1. 搜路由注册 + 手工 curl 一遍关键接口看响应结构:

``bash for p in /api/items /api/users /api/auth/login; do curl -s -o - -w "\n[HTTP %{http_code}]\n" http://localhost:8080$p done ``

  1. 抽取 3–5 条错误响应,对比形状是否一致。
  2. 分页接口:curl "http://localhost:8080/api/items?page=1&size=10"; 看是否识别。

证据要求

  • 至少 5 条 curl log 覆盖成功 / 失败 / 分页三类。
  • 至少 2 条 file:line 引用统一错误处理中间件(或反面证据说明缺失)。
  • 一份错误响应结构对比表(报告里 3–5 行即可)。

4. 测试(权重 12)

关注点

  • 是否有自动化测试(单元 + 集成)。
  • 关键路径(登录、核心写操作、错误分支)是否覆盖。
  • 测试是否真跑通,不是 skip 一片。
  • 覆盖率数据。

锚点(基于测试覆盖率)

  • 10:覆盖率 ≥70%;覆盖关键 happy + error 路径;集成测试真打接口;CI 本地 npm test / pytest / go test 一次绿。
  • 9:覆盖率 ≥65% 且 happy + error 路径都覆盖,集成测试真打接口,仅 1 个次要错误分支(如幂等冲突)未测。
  • 8:覆盖率 ≥55%,主要 happy + 核心 error 分支都有集成测试,次要模块仅单元测试未做集成覆盖。
  • 7:覆盖率 ≥50%;主要 happy path 覆盖,error 路径少量。
  • 5:覆盖率有但多数只 happy path,或集成测试缺失仅单元测试。
  • 3:几乎没有测试(<20% 或只有 1–2 个 sanity test)或大量 skip / TODO。

取证方法

  1. 按栈跑测试 + 覆盖率:

``bash npm test -- --coverage # Node pytest --cov --cov-report=term # Python go test -cover ./... # Go ./gradlew test jacocoTestReport # Java ` 把输出 tee 到 .grading/probes/<team>-test.log`。

  1. 统计测试文件数与被跳过的用例:

``bash grep -rnE "\.skip|@pytest\.mark\.skip|t\.Skip\(|xit\(|xdescribe\(" . ``

  1. 抽一个 error 路径测试用例读 5–10 行,看是否真的断言了错误码而非只跑过。

证据要求

  • 1 份 test 输出 log(含覆盖率数字)。
  • 至少 3 条 file:line 引用测试文件(good case)或缺失(bad case)。
  • 一条 skip 统计:skip 数 / 总数

5. 数据建模(权重 10)

关注点

  • schema / 模型定义与 spec 对齐(字段、类型、约束、关系)。
  • 主键、外键、唯一索引、必要索引齐全。
  • 有 migration 脚本 / 版本化,不是手改表。
  • 字段命名、类型选择合理(时间用 timestamptz / datetime,金额不用 float)。

锚点

  • 10:schema 与 spec 100% 对齐;外键 / 唯一约束 / 索引完整;有 migration 版本链;命名和类型都合理。
  • 9:schema 对齐且约束完整,migration 版本链干净,仅缺 1 个次要查询索引(不影响主热路径)。
  • 8:字段类型 / 外键 / 主键 / 唯一约束都对,migration 存在,但 1 处复合索引缺失或 1 个字段用 text 而 spec 建议 varchar(n)
  • 7:schema 对齐,但缺 1–2 个索引或 1 处外键缺失,migration 有但不规整。
  • 5:schema 勉强能跑,多处字段类型错位(如用 varchar 存时间),索引全无。
  • 3:无 schema 定义 / 无 migration,或字段跟 spec 大幅偏离。

取证方法

  1. 找 schema 文件:

``bash find . -type f \( -name ".sql" -o -name "schema.prisma" -o -name "models.py" -o -name "entity.go" -o -name "*Entity.java" \) | head find . -type d -name "migrations" -o -name "migrate" ``

  1. 对每个核心表记录:字段 / 类型 / 约束 / 索引,对照 spec。
  2. 看 migration 目录是否有版本编号、是否 down script 齐全。

证据要求

  • 至少 3 条 file:line 引用 schema / model / migration 文件。
  • 一份核心表字段对照表(表名 / 字段 / 类型 / spec 期望 / 实际)。
  • 索引清单(从 CREATE INDEX 或 ORM 注解里抽)。

6. 代码分层(权重 10)

关注点

  • controller / handler / route 层只做参数解析和响应包装,不写业务。
  • service / usecase 层承载业务逻辑,不直接写 SQL。
  • repository / dao 层封装数据访问,controller 不绕过它直接拿 ORM。
  • 跨层依赖方向单一(controller → service → repo),不反向依赖。

锚点(基于 grep controller 里是否有 SQL/ORM 调用)

  • 10:三层清晰;controller 内无 SQL / ORM 调用;service 内无 http 响应组装;循环依赖为 0。
  • 9:controller 全部是薄壳(参数解析 + 转发 service),service 不碰 http,仅 1 处 repo 里拼了一点点业务判断应归 service。
  • 8:分层整体清晰,controller 基本只做转发,但 1 个 handler 里直接调了一次 ORM(如简单 count 查询绕过 repo),依赖方向仍单向。
  • 7:整体分层,但有 1–2 个 controller 顺手调了 ORM 或在 handler 里拼了复杂业务。
  • 5:有分层意图但大量泄漏:controller 里经常直接 prisma.xxx / db.query,service 里写 res.json(...)
  • 3:一锅端,所有逻辑都在 route handler 里,没有 service / repo 概念。

取证方法

  1. Grep controller 目录有无直接 SQL / ORM:

``bash # Node/TS grep -rnE "prisma\.|knex\(|db\.query|createQueryBuilder|sequelize\." src/controllers src/routes src/handlers 2>/dev/null # Python grep -rnE "session\.query|db\.execute|cursor\.execute" app/api app/routes 2>/dev/null # Go grep -rnE "db\.Query|db\.Exec|gorm\.|sqlx\." internal/handler internal/controller 2>/dev/null `` 命中越多越说明泄漏。

  1. 抽 2 个 controller 文件读头 30 行,看逻辑是否"薄"。
  2. 画依赖箭头(口述即可):controller -> service -> repo 是否单向。

证据要求

  • 至少 1 组 grep 统计:「controller 目录命中 SQL/ORM 次数 / 总 controller 文件数」。
  • 至少 3 条 file:line(好的 service / 坏的泄漏各举例)。
  • 一句话描述依赖方向。

7. 性能(权重 8)

关注点

  • 关键接口响应时间(p50 / p95 / p99)。
  • 无 N+1 查询(循环里发请求 / query)。
  • 合理的索引支撑主查询。
  • 有必要的缓存 / 批处理(spec 要求时)。

锚点(基于 autocannon p95,10 并发 5 秒,本地)

  • 10:主 GET 列表接口 p95 < 100ms;写接口 p95 < 300ms;N+1 零命中。
  • 9:主 GET p95 < 150ms,写 p95 < 400ms,N+1 零命中,仅 1 处次要聚合查询未加索引但 p95 仍在可接受区间。
  • 8:主 GET p95 < 200ms,写 p95 < 500ms,热路径无 N+1,冷路径(如后台导出)存在 1 处 N+1 但不影响生产体验。
  • 7:p95 < 300ms;偶有 1–2 处 N+1 但不在热路径。
  • 5:p95 < 800ms;热路径明显 N+1 或缺索引。
  • 3:p95 ≥ 800ms 或直接接口超时 / 打挂服务。

取证方法

  1. bash ./probes/probe-performance.sh <team> <base-url> <path>,读 p50/p95/p99。
  2. 检查 N+1:

``bash # 找循环里调 query / repo 的反模式 grep -rnE "for\s|forEach|\.map\(" src/ | grep -E "repo\.|query|findBy" ``

  1. 对主查询看 EXPLAIN(可选):EXPLAIN SELECT ... FROM items WHERE ...

证据要求

  • 1 份 <team>-autocannon.log + 1 份 <team>-perf-summary.txt(含 p50/p95/p99)。
  • 至少 2 条 file:line 体现 N+1 或优化手段(或反面证据)。
  • 一条关于索引 / 缓存 / 批处理的结论。

8. 可观测性(权重 7)

关注点

  • 有结构化日志(JSON 或至少 key=value),不是满屏 print / console.log
  • 关键操作有 trace:请求 ID / 用户 ID / 业务动作。
  • 错误日志带上下文(请求体摘要、堆栈)。
  • 有 metrics / health 端点(起码 /health)。

锚点(基于 console.log / print 占日志语句比例)

  • 10:统一日志库(pino / zap / loguru / slog / logback);所有接口有请求进入 + 结束日志 + requestId;print/console.log 数 ≤ 2。
  • 9:统一日志库 + 全接口 requestId + 结构化日志,print/console.log ≤ 5 且都在非生产路径(如 seed 脚本),/health 可用。
  • 8:结构化日志覆盖主要接口且有 requestId,但 1–2 个次要接口缺请求结束日志,print/console.log 残留 5–10 处。
  • 7:有日志库,但部分关键路径漏打,或偶有 console.log 残留(3–10 处)。
  • 5:日志库有但只打错误;print/console.log 占比 > 30%。
  • 3:全靠 print / console.log,没有 requestId / 结构化,错误靠 try/catch 打一句。

取证方法

  1. 统计日志风格:

``bash grep -rncE "console\.log|console\.debug|\bprint\(" src/ | sort -t: -k2 -n -r | head grep -rncE "logger\.|log\.(info|warn|error|debug)" src/ | sort -t: -k2 -n -r | head `` 算两个总数相除得到 "jank 比例"。

  1. /health/metricscurl -i http://localhost:8080/health
  2. 抽一个 error 分支读,看是否打了上下文。

证据要求

  • 一组 grep 统计(logger 数 vs print/console.log 数)。
  • 至少 2 条 file:line 引用日志配置或关键调用。
  • 1 条 /health/metrics 的 curl 响应。

9. 文档(权重 6)

关注点

  • README 含启动步骤、依赖、环境变量、示例调用,不假设读者已懂。
  • 接口文档(OpenAPI / Swagger / Postman collection / README 章节)存在且与实现同步。
  • 示例 curl / 请求体可直接复制跑通。
  • 关键设计决策(分层、鉴权、数据模型)有一小段说明。

锚点

  • 10:README 从零到跑通 5 分钟内完成;OpenAPI 文档完整、可在 /docs 打开;有架构图或 ADR。
  • 9:README 跑通流畅,OpenAPI 完整且与实现一致,有架构图,仅 ADR / 设计决策说明简略(1–2 条一句话带过)。
  • 8:README 启动步骤 + 环境变量 + 示例 curl 齐全,OpenAPI 可打开且主接口字段对齐,无架构图但有一段分层 / 鉴权文字说明。
  • 7:README 够用,接口文档有但 1–2 处字段过期;无架构图。
  • 5:README 只有 "npm install && npm start",接口文档靠读代码。
  • 3:几乎无文档,spec 之外无任何说明。

取证方法

  1. 按 README 从零跑一遍(或复查步骤 1 的启动体验),记录卡点。
  2. 找接口文档:

``bash find . -type f \( -name "openapi.yml" -o -name "swagger.json" -o -name ".postman_collection.json" \) curl -i http://localhost:8080/docs # Swagger UI curl -i http://localhost:8080/openapi.json # FastAPI / 多数框架 ``

  1. 抽示例 curl 从 README 粘出来跑一次,记录是否通。

证据要求

  • 至少 2 条 README.md:linedocs/*.md:line 引用。
  • 1 次"照 README 跑"的实测结果(通 / 不通 + 卡点)。
  • 接口文档位置 + 1 条示例 curl log。

产出 checklist

评分结束前逐项勾选,任一不通过就回到相应步骤补齐:

  • .grading/reports/<team>-backend.md 已按本文件 ## 报告模板 段结构填完,9 个维度全部评完(或明确标 N/A)。
  • .grading/reports/<team>-backend.json 已产出,并通过 npx ajv-cli validate -s skills/score-schema.json 校验。
  • 每个维度都有证据引用,且满足本文件 ## 证据硬约束 段(≥7 / ≤4 分 ≥2 条;5–6 分 ≥1 条)。
  • .grading/probes/ 含:<team>-robustness-summary.md + 各 probe <team>-<probe-name>.log<team>-autocannon.log + <team>-perf-summary.txt<team>-test.log<team>-curl.log
  • 报告末尾给出"最该优先修的 3 件事",每条对应到具体维度和 file:line。