diff --git a/pipeline_bidding/bid_flow.py b/pipeline_bidding/bid_flow.py index 8b067c4..ef065be 100644 --- a/pipeline_bidding/bid_flow.py +++ b/pipeline_bidding/bid_flow.py @@ -104,7 +104,11 @@ DIM_UPSTREAM = { "scoring": (), "quals": ("scoring",), "reqs_outline": ("scoring",), - "cost_benefit": ("scoring", "quals", "reqs_outline"), + # 2026-09-15 用户裁定:资质有无/资质QC不阻塞标书推进。cost_benefit 测算 + # (保证金/预算/付款条件)实际依据评分项与投标文件要求,不依赖资质清单; + # 且 BID_FLOW_STAGES 声明层 analysis_cost_benefit.deps 本就无 analysis_quals + # ——机制层与声明层对齐(原不一致:quals QC 卡住连报价章全链冻结)。 + "cost_benefit": ("scoring", "reqs_outline"), } # 维度 → 落库表(落库核验用;reqs_outline 维度同时写两张表) @@ -119,10 +123,12 @@ DIM_TABLES = { # 通过 QC——某一维度 QC 卡住只阻塞依赖它的章节,其余章节照常流转。 # 公共事实:章节骨架(bid_chapters)本身就是章节,OUTLINE 过了章节才可信; # REQS(投标文件格式要求)决定章节怎么写,所有章节都要。 +# 2026-09-15 用户裁定:资质有无/QC过否不阻塞标书编写——business 移除 QUALS 依赖 +# (资质缺口走 human_docs_request 待办通知人类,属信息流不是门禁流)。 CH_SECTION_DEPS = { "technical": (QC_TYPE_SCORING, QC_TYPE_REQS, QC_TYPE_OUTLINE), "price": (QC_TYPE_SCORING, QC_TYPE_COST, QC_TYPE_OUTLINE), - "business": (QC_TYPE_QUALS, QC_TYPE_REQS, QC_TYPE_OUTLINE), + "business": (QC_TYPE_REQS, QC_TYPE_OUTLINE), } @@ -999,15 +1005,18 @@ async def reconcile_project(sor, project_id, project_name=""): await sor.sqlExe("COMMIT", {}) if prev: prev_imp = rec_to_dict(prev[0]).get("improvement") or "" + # ⚠ audit_log 表只有 created_at(models/audit_log.json 与库一致), + # 曾写 updated_at → 1054 使整个 reconcile_project 崩溃、全项目派发停摆 + # (2026-09-15 甘肃项目实测:第3轮复审派发路径首次触雷,累计崩 175 次)。 fx = await sor.sqlExe( - "SELECT action, entity, detail, updated_at FROM audit_log " + "SELECT action, entity, detail, created_at FROM audit_log " "WHERE tenant_id=${p}$ AND entity IN ('bid_chapters','bid_doc_requirements'," "'bid_scoring_items','bid_qualifications','bid_cost_benefit') " - "ORDER BY updated_at DESC LIMIT 30", + "ORDER BY created_at DESC LIMIT 30", {"p": project_id}) await sor.sqlExe("COMMIT", {}) fixes = "\n".join( - "- [%s] %s/%s: %s" % (rec_to_dict(f).get("updated_at", "")[:16], + "- [%s] %s/%s: %s" % (rec_to_dict(f).get("created_at", "")[:16], rec_to_dict(f).get("entity", ""), rec_to_dict(f).get("action", ""), (rec_to_dict(f).get("detail") or "")[:80]) @@ -1030,7 +1039,8 @@ async def reconcile_project(sor, project_id, project_name=""): # ── B. 资料准备(知识库资质匹配 + 人类文件清单)── # 2026-09-03 章节级依赖:资料准备是商务章(business)的上游依赖, # 不再阻塞全局流转——技术/报价章在上游 QC 通过后立即开写。 - # 2026-09-11 流程裁剪:prep 被裁 → 不派;商务章 biz_ok 判定同步视为完成。 + # 2026-09-11 流程裁剪:prep 被裁 → 不派。 + # 2026-09-15 起资料准备不再阻塞商务章(资质缺口=信息流,见下方 C 段注释)。 prep_done = (not _stage_on(STAGE_PREP)) or await has_done_task(sor, project_id, R_PREP) if _stage_on(STAGE_PREP) and n_quals > 0 and not prep_done: if await count_open_tasks(sor, project_id, role=R_PREP) > 0: @@ -1054,8 +1064,12 @@ async def reconcile_project(sor, project_id, project_name=""): # 其余章节照常流转(根治:此前五类 QC 全过才放行,cost_benefit 一个维度慢 # 导致全部章节冻结)。商务章额外等资料准备完成。 qc_passed = await _qc_passed_eff() - hd_pending = await find_human_task(sor, project_id, HT_HUMAN_DOCS, status="pending") - biz_ok = (prep_done or n_quals == 0) and not hd_pending + # 2026-09-15 用户裁定:资质文件待办(human_docs_request)不再阻塞任何章节编写。 + # 原 biz_ok=(prep_done or n_quals==0) and not hd_pending 会把「人类没上传资质文件」 + # 变成商务章硬门禁——用户明确说忽略也不管用(答复还会被 agent QC 退回,见 + # human_task_capability 的人类权威规则)。资质缺口是信息流(待办通知),不是门禁流。 + # prep(资料准备)也不再挡商务章:与声明层对齐(chapter_write.deps 只有 + # analysis_reqs_outline),写者需要资质信息时自查知识库/待办清单。 created_w = 0 waiting_deps = {} for c in chapters: @@ -1075,9 +1089,6 @@ async def reconcile_project(sor, project_id, project_name=""): if unmet: waiting_deps.setdefault("/".join(sorted(unmet)), []).append(str(c.get("chapter_no"))) continue - if section == "business" and not biz_ok: - waiting_deps.setdefault("bid_prep(资料准备)", []).append(str(c.get("chapter_no"))) - continue stage = "revise" if c.get("status") == CH_REJECTED else "write" tid = await create_role_task( sor, project_id, role,