feat(check): 同一行按单元格;业绩条款不再误跳;重复条目只合并显示(C5 修订)

- 送给模型的「同一行」在有框线的表格里按单元格分行
- 「以合同签订时间为准」不再被当成「签订合同时才交」,要附合同的业绩评分项照常查
- 重复的要求各自判断,问题接口给出 duplicate_ids,查标页同组折叠显示、一起处理
- 决策 C5 修订记入 decisions.md;开发集召回 74%、致命级 25/32

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
YANG JIANKUAN
2026-11-04 18:00:24 +08:00
parent df3ebbb6a1
commit 4a5421a412
12 changed files with 111 additions and 13 deletions

View File

@@ -360,3 +360,19 @@ async def test_recheck_reuses_unchanged_model_results(user: User) -> None:
assert n == 0
results = (await user.get(f"/check-runs/{second['id']}/item-results")).json()["items"]
assert all(r["bid_citations"] for r in results if r["verdict"] == "responded") # 沿用的结论证据也找回来了
async def test_duplicate_requirements_are_checked_separately_and_grouped_for_display(user: User) -> None:
"""重复的要求(决策 C5 修订):各自判断、各出一个问题,问题接口给出同组的其他问题,界面折叠显示。"""
project_id, tender_id = await project_with_tender(user)
await run_parse(user, tender_id)
for _ in range(2):
r = await user.post(
f"/projects/{project_id}/checklist/items",
json={"categories": ["qualification"], "title": "须提供船舶防爆检验报告"},
)
assert r.status_code == 201, r.text
bid = await upload_bid(user, project_id, "bid-mini-v1.pdf")
run = await run_check(user, project_id, [bid["id"]])
dup = [f for f in await findings(user, run["id"]) if "船舶防爆" in f["title"]]
assert len(dup) == 2 and dup[0]["duplicate_ids"] == [dup[1]["id"]] and dup[1]["duplicate_ids"] == [dup[0]["id"]]

View File

@@ -279,3 +279,17 @@ async def test_over_budget_items_are_undetermined_without_model_calls() -> None:
results, stats = await match.run(ctx, uuid4(), items, [d], progress, budget=10)
(r,) = results.values()
assert (r.verdict, r.reason_code, stats.calls, stats.over_budget) == ("undetermined", "over_budget", 0, 1)
def test_award_time_wording_is_not_award_stage_evidence() -> None:
"""「以合同签订时间为准」是业绩的时间口径,不是「签订合同时才交」:要附合同的评分项照常查(check-eval §9)。"""
from bid_doctor.check import applicability
scoring = {
"categories": ["scoring"],
"title": "项目业绩(10 分)",
"requirement_text": "提供2023年以来类似项目业绩,以合同签订时间为准,需提供合同复印件。",
}
assert applicability.decide(scoring, {}).match
later = {**scoring, "requirement_text": "签订合同时提供业绩合同原件备查。"}
assert applicability.decide(later, {}).skip == "scoring"