PMFlow/docs/superpowers/plans/2026-04-17-unified-project-...

39 KiB
Raw Blame History

Unified Project Operating Context And Three-Lane Handoff Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 先补一层统一的 Project Operating Context,再把 internal / external / planmode 收口成一个统一入口下的三条执行能力,让 PMFlow 变成“懂当前项目、能取外部资料、能继续推进计划”的项目型 AI,而不是三套互相打架的聊天模式。

Architecture: 本次不把三条链继续拆成三个彼此孤立的产品模式,而是在 ingress 先建立统一上下文层,再产出单次 execution_recipe。internal 负责内部对象事实与项目适配画像,external 负责外部可核验资料与推荐,planmode 只作为后续 handoff 能力接棒,默认策略固定为“先给判断结论,再明确给一个进入 plan 的下一步”。这里的 planmode 只是对话层简称;落到 repo 真名时,统一指 plan_mode 顶层 gate 以及其后的 requirement_plan / draft_pending_confirm 主链,不额外引入第四个 runtime route 名。外部推荐允许消费内部 fit brief 生成检索计划,但最终外部答案正文仍必须只由外部证据支撑。

Tech Stack: FastAPI, Pydantic, SQLAlchemy 2.0, AdaptiveRAG, AI PMO Kernel, External Query Gateway, React 18, TypeScript, React Query, Vitest, pytest


Scope And Guardrails

  • 本计划在真正实现前,必须先补一份 Feishu 问题清单 + 黄金对话样例,把用户已知痛点收成可回归样本;没有这一步,不允许直接进入三路整合实现。
  • 本计划的统一上下文默认范围固定为:
    • 当前项目
    • 当前仓库摘要
    • 当前线程 / 页面 / reply 强锚点
  • 这里的“当前仓库”按 repo 现状必须理解成“运行时可取得的 codebase snapshot / repo summary”,不是默认已经具备全 workspace、全语言、全目录的完整理解;当前 codebase overview 仍以 backend snapshot 为主,若后续扩展前端或全仓库画像,应作为显式增量实现。
  • v1 不默认扩到“用户名下所有项目”或“整个租户 portfolio”,避免重新回到全局聊天串线。
  • 默认多意图策略固定为:
    • A:先给基于项目的判断结论
    • 再明确给一个“要不要基于这个进入 plan”的下一步
  • 强锚点仍优先于旧 session 和记忆续写:
    • reply/thread
    • 页面 project_id
    • 显式 work_item_id
    • 已绑定 active requirement / draft context
  • 默认 A 只适用于未命中显式 plan_mode gate 的 recommendation / 判断题;一旦命中 active requirement context、draft confirm、显式 reply/thread 或 work_item_id 强锚点,必须继续留在 repo 既有的 requirement_plan / draft_pending_confirm 主链,不能被“先答再给 plan 入口”抢走。
  • 严禁回到散落运行时语义拍板,这是一条硬约束:
    • 不准新增关键词仓
    • 不准新增正则拍板 helper
    • 不准新增“看到某些词就切 internal / external / plan”的硬编码语义分流
    • 不准在执行层靠“问题长相”重判 internal / external / plan
    • 不准把飞书问题修成一堆 bypass 规则
  • repo 里其它路径仍可能残留历史 keyword / lookup 工具;这轮 unified ingress 和三路 handoff 不得复用它们来做来源裁决、plan 夺权或飞书短句分流。
  • external 推荐允许使用 internal 侧生成的 fit brief 作为“检索约束输入”,但 final external answer 仍必须遵守 external-only 展示合同:
    • fit brief 只属于 pre-retrieval planning input,不放宽 repo 现有的单一 source_policy 合同,也不允许同一轮 query 混搜 internal + external 证据
    • presenter 不重新注入 system_snapshot / user_snapshot / project_context / internal context_blocks
    • 客户可见正文不混写内部事实充当外部证据,且最终展示仍必须遵守 customer_safe / admin_debug 双视图边界
  • external recommendation 必须显式区分外部证据状态:
    • evidence_found
    • verifier_blocked
    • no_relevant_results
    • retrieval_failed
  • 下面这些 impact/action 约束属于这份计划的 v1 product guardrail,不应被误读成 repo 现状已经完整落地的 runtime contract;实现时若与现有对象模型冲突,应先回到正式业务对象和 guardrail 真源校正。
  • external recommendation 的“影响判断”必须独立于“检索状态”表达:
    • direct_match
    • indirect_only
    • no_direct_impact
  • 外部影响默认优先绑定正式项目对象;若当前项目存在正式 signal source / capability 锚点,则优先绑定这些对象。
  • 若外部证据只能说明行业背景、技术可行性或泛市场趋势,而不能精确命中当前项目正式对象,只能返回 indirect_only,禁止伪装成“已命中项目推荐”。
  • 外部动作只能落在:
    • 范围调整
    • 规则/口径核验
    • 接口兼容性验证
    • 监控补点
    • 数据映射补齐
  • 外部动作禁止涉及:
    • 招聘
    • 人员分配
    • 负责人指派
    • 组织调整
    • 直接修改项目/信号源状态
  • planmode 不再作为第一反应默认吞掉“适不适合 / 推荐什么 / 要不要接”这类问题;只有显式强动作锚点或统一 ingress recipe 判定为 explicit_plan_now 时才直接接管。

Non-Goals For This Plan

  • 不做 portfolio-wide 全局 AI 助手
  • 不做“只要像建议题就自动进 plan”的激进策略
  • 不做在线自动学习路由阈值
  • 不在 v1 里引入新的自由工具调用或自由联网 agent
  • 不把 external answer presenter 改成混合 internal + external 的单 prompt
  • 不以关键词/正则/硬编码语义规则作为飞书问题的修复手段

Baseline File Map

Context Layer

  • Create: backend/app/schemas/project_operating_context.py
    • 统一定义 ProjectOperatingContext、ProjectFitBrief、ExecutionRecipe 等结构化合同。
  • Create: backend/app/services/ai_pmo/project_operating_context_builder.py
    • 从 current_project_id / current repo snapshot / page anchor / thread anchor / world model 组装统一上下文包。
  • Modify: backend/app/services/ai_pmo/ai_pmo_kernel_ingress_flow/mixin_entry.py
    • 在 ingress 入口建立并透传统一上下文,而不是先各走各的语义判断。
  • Modify: backend/app/services/ai_pmo/ai_pmo_kernel_world_model_core/mixin_builder.py
    • 暴露 resolved_project_id 之外的项目运行态摘要,供统一上下文层使用。
  • Modify: backend/app/services/ai_pmo/ai_pmo_kernel/mixins/world_model/ai_pmo_kernel_mixins_world_model_session.py
    • 持久化最小必要的 project operating context snapshot,避免 session 只记 current_project_id 太粗。

Unified Query Handoff

  • Modify: backend/app/services/adaptive_rag_service/mixin_query_route_gateway.py
    • 在保留 route_mode / effective_source_policy / reply_contract / probe_snapshot 合同的前提下,叠加更高层的 execution_recipe 输出。
  • Modify: backend/app/services/adaptive_rag_service/mixin_core_intent.py
    • 让 query semantics 可消费统一上下文和 handoff hint,而不是只看问题文本。
  • Modify: backend/app/services/adaptive_rag_service/mixin_answer_mainline.py
    • 按 execution_recipe 接棒 internal / external / plan-offer,而不是三路各自隐式争抢。
  • Modify: backend/app/services/adaptive_rag_service/mixin_followup_anchor.py
    • follow-up 续接优先基于统一上下文,而不是仅靠 session state。

Internal Fit Brief

  • Create: backend/app/services/adaptive_rag_service/mixin_project_fit_brief.py
    • 负责从项目对象、功能目录、repo 概览、当前能力缺口生成 customer-safe fit brief。
  • Modify: backend/app/services/adaptive_rag_service/service.py
    • 接入新的 fit-brief mixin。
  • Modify: backend/app/services/adaptive_rag_service/mixin_manifest_feature_p1.py
    • 暴露 feature catalog 命中摘要给 fit brief 使用。
  • Modify: backend/app/services/project_knowledge_graph_service/mixin_codebase_overview.py
    • 把 codebase overview 从单纯 backend Python 统计扩成可用于“当前仓库摘要”的最小基线。
  • Modify: backend/app/services/context_cache_service/mixin_user_snapshot.py
    • 只补 customer-safe 的当前项目参与状态,不扩成 portfolio 大杂烩。

External Recommendation

  • Modify: backend/app/services/external_query_service/schemas.py
    • 给 external plan / execution 增加 fit_brief_summary / recommendation_goal / project_scope_hint 等检索约束字段。
  • Modify: backend/app/services/external_query_service/planner.py
    • 让 planner 可以吃 fit brief,生成更贴项目的 query plan。
  • Modify: backend/app/services/external_query_service/gateway.py
    • 保留 adapter/verifier 主链,同时把 fit_brief 作为 planner meta 一部分审计。
  • Modify: backend/app/services/external_query_service/presenter.py
    • 明确拒绝消费 internal evidence;只消费 external packet,但支持输出“适配判断”模板位。
  • Modify: backend/app/services/adaptive_rag_service/mixin_answer_payload.py
    • 把 fit brief 和 external evidence 拆成不同 payload 区块,而不是混成一段答案。

Plan Handoff And UX

  • Modify: backend/app/services/adaptive_rag_service/mixin_answer_payload.py
    • 在回答 payload 里增加 recommended_next_step / can_enter_plan / handoff_mode。
  • Modify: backend/app/services/integrations/feishu_signal_pipeline/mixin_process_message_default.py
    • 飞书默认按 A 策略:先答,再给一个明确的 plan handoff 入口。
  • Modify: backend/app/services/integrations/feishu_signal_pipeline/mixin_context_prefill_a.py
    • 把当前项目 / 当前线程 / 当前 repo 级别提示收进统一上下文,不在飞书旁路拼接脏 context。
  • Modify: frontend/src/components/ai-steward/FloatingQAChat.tsx
    • 展示 unified context、fit brief、外部推荐结果和“进入 plan”下一步。
  • Modify: frontend/src/components/ai-steward/QAConversationStatusBar.tsx
    • 展示当前 operating context,而不是只展示“内部还是外部”。
  • Modify: frontend/src/components/ai-steward/QAResponseMetaCard.tsx
    • 新增“项目画像 / 外部证据 / 下一步”三段式元信息卡。

Tests

  • Create: backend/tests/test_project_operating_context_contract.py
  • Modify: backend/tests/test_ai_pmo_ingress_mainline.py
  • Modify: backend/tests/test_rag_route_contract.py
  • Modify: backend/tests/test_adaptive_rag_mainline_contract.py
  • Modify: backend/tests/test_adaptive_rag_external_followup_contract.py
  • Modify: backend/tests/test_external_query_planner.py
  • Modify: backend/tests/test_internal_query_context_packet.py
  • Modify: backend/tests/test_ai_pmo_qa_http_mainline_e2e.py
  • Modify: backend/tests/test_feishu_pipeline_queue_contract.py
  • Create: frontend/src/components/ai-steward/QAResponseMetaCard.test.tsx
  • Modify: frontend/src/components/ai-steward/FloatingQAChat.test.tsx

Docs

  • Modify: CLAUDE.md
  • Modify: docs/architecture/ARCHITECTURE_INVENTORY.md
  • Modify: docs/architecture/CODE_REFERENCE_INDEX.md
  • Modify: docs/architecture/RUNTIME_GUARDRAILS.md
  • Create: docs/superpowers/specs/2026-04-17-feishu-problem-inventory.md

Task 0: Build The Feishu Problem Inventory Before Any Runtime Changes

Files:

  • Create: docs/superpowers/specs/2026-04-17-feishu-problem-inventory.md

  • Modify: backend/tests/test_feishu_pipeline_queue_contract.py

  • Modify: backend/tests/test_ai_pmo_qa_http_mainline_e2e.py

  • Modify: backend/tests/test_adaptive_rag_external_followup_contract.py

  • Modify: docs/architecture/RUNTIME_GUARDRAILS.md

  • Step 1: Collect the current Feishu failure classes from existing code, tests, and guardrails

The inventory must explicitly classify at least these buckets:

1. reply/thread 锚点丢失或绑错
2. internal / external / planmode 误分流
3. 外部问答后续一句话错误掉进 internal 或 plan
4. “适不适合 / 推荐什么 / 怎么推进” 这类复合问题被拆错
5. 同群同人 scope 串线
6. 旧 active context 劫持新问题
7. 明明该继续 plan,却掉回 query.answer
8. 明明只是问结论,却被直接拉进 plan
9. 卡片动作和纯文本回复行为不一致
10. Web 与 Feishu 对同一句话给出不同链路
  • Step 2: Write 10-15 gold conversation cases in the inventory doc

Each case must include:

- 场景名
- 初始上下文
- 用户原话
- 当前错误行为
- 目标行为
- 应命中的 lane / handoff
- 验证方式

At least one case must cover each of:

  • 纯内部系统问题

  • 纯外部事实问题

  • “先判断,再给 plan 入口”

  • 显式强锚点直接进 plan

  • reply/thread 明确续问

  • 同群同人但切新话题

  • Step 3: Convert the highest-value cases into failing regression tests

Required minimum:

  • 2 cases in test_feishu_pipeline_queue_contract.py
  • 2 cases in test_ai_pmo_qa_http_mainline_e2e.py
  • 1 case in test_adaptive_rag_external_followup_contract.py

These tests should fail under the current runtime if the problem still exists, or be marked as expected coverage gaps if the exact behavior is not yet implemented.

  • Step 4: Record explicit acceptance criteria in RUNTIME_GUARDRAILS.md

Add a short section saying the unified context / three-lane handoff work must be accepted against the Feishu problem inventory, not just architecture conformance.

Also add an explicit line:

This work is not allowed to introduce new keyword routing, regex routing, or hard-coded semantic split helpers.
  • Step 5: Run the inventory-aligned tests and capture the baseline

Run:

cd /Users/yuuuu/Documents/AIProjectManger && PYTHONPATH=backend backend/venv/bin/python -m pytest \
  backend/tests/test_feishu_pipeline_queue_contract.py \
  backend/tests/test_ai_pmo_qa_http_mainline_e2e.py \
  backend/tests/test_adaptive_rag_external_followup_contract.py -v

Expected:

  • either explicit FAIL on known bad cases
  • or explicit PASS with coverage notes in the inventory doc

This baseline becomes the “before” snapshot for the rest of the plan.

Task 1: Lock The Unified Project Operating Context Contract

Files:

  • Create: backend/tests/test_project_operating_context_contract.py

  • Modify: backend/tests/test_ai_pmo_ingress_mainline.py

  • Modify: backend/tests/test_rag_route_contract.py

  • Step 1: Write the failing contract tests for the new context packet

Add tests that lock a minimal packet such as:

context = ProjectOperatingContext(
    current_project_id=17,
    current_repo_scope="pmflow-main",
    page_anchor={"project_id": 17, "origin": "pathname"},
    thread_anchor={"reply_to_message_id": "om_xxx"},
    fit_scope="current_project_and_repo",
    decision_strategy="answer_then_offer_plan",
)

assert context.current_project_id == 17
assert context.current_repo_scope == "pmflow-main"
assert context.decision_strategy == "answer_then_offer_plan"
  • Step 2: Add ingress tests that require the packet to be attached before query execution

Lock that AIPMOKernelService.process_ingress() or its contract builder must produce:

assert ingress_options["project_operating_context"]["current_project_id"] == 17
assert ingress_options["project_operating_context"]["fit_scope"] == "current_project_and_repo"
  • Step 3: Lock route-contract tests so /rag/ask receives the unified packet

The HTTP-facing RAG contract should prove:

assert payload["project_operating_context"]["current_project_id"] == 17
assert payload["project_operating_context"]["decision_strategy"] == "answer_then_offer_plan"
  • Step 4: Run focused tests and confirm they fail before implementation

Run:

cd /Users/yuuuu/Documents/AIProjectManger && PYTHONPATH=backend backend/venv/bin/python -m pytest \
  backend/tests/test_project_operating_context_contract.py \
  backend/tests/test_ai_pmo_ingress_mainline.py \
  backend/tests/test_rag_route_contract.py -v

Expected: FAIL because the packet/schema/builder do not exist yet.

Task 2: Build And Persist Project Operating Context At Ingress

Files:

  • Create: backend/app/schemas/project_operating_context.py

  • Create: backend/app/services/ai_pmo/project_operating_context_builder.py

  • Modify: backend/app/services/ai_pmo/ai_pmo_kernel_ingress_flow/mixin_entry.py

  • Modify: backend/app/services/ai_pmo/ai_pmo_kernel_world_model_core/mixin_builder.py

  • Modify: backend/app/services/ai_pmo/ai_pmo_kernel/mixins/world_model/ai_pmo_kernel_mixins_world_model_session.py

  • Test: backend/tests/test_project_operating_context_contract.py

  • Test: backend/tests/test_ai_pmo_ingress_mainline.py

  • Step 1: Add the minimal Pydantic schema

Create a focused schema module:

class ProjectOperatingContext(BaseModel):
    context_version: str = "v1"
    current_project_id: int | None = None
    current_repo_scope: str | None = None
    page_anchor: dict[str, Any] = Field(default_factory=dict)
    thread_anchor: dict[str, Any] = Field(default_factory=dict)
    fit_scope: Literal["current_project_and_repo"] = "current_project_and_repo"
    decision_strategy: Literal["answer_then_offer_plan"] = "answer_then_offer_plan"
  • Step 2: Implement a small builder that composes current project, repo, and anchors

The builder should only consume stable inputs already present in runtime:

  • resolved_project_id
  • current repo / codebase scope
  • explicit page anchor
  • explicit thread anchor

Do not infer portfolio scope or global user history here.

  • Step 3: Attach the packet in kernel ingress before any query route or plan handoff logic

Make mixin_entry.py the single place that builds and stores the packet in ingress_options.

  • Step 4: Persist only the minimal session snapshot

Store only:

  • current_project_id
  • current_repo_scope
  • last_page_anchor
  • last_thread_anchor

Do not persist a giant free-form context blob.

  • Step 5: Re-run focused tests and confirm they pass

Run:

cd /Users/yuuuu/Documents/AIProjectManger && PYTHONPATH=backend backend/venv/bin/python -m pytest \
  backend/tests/test_project_operating_context_contract.py \
  backend/tests/test_ai_pmo_ingress_mainline.py -v

Expected: PASS

  • Step 6: Commit
git add backend/app/schemas/project_operating_context.py \
  backend/app/services/ai_pmo/project_operating_context_builder.py \
  backend/app/services/ai_pmo/ai_pmo_kernel_ingress_flow/mixin_entry.py \
  backend/app/services/ai_pmo/ai_pmo_kernel_world_model_core/mixin_builder.py \
  backend/app/services/ai_pmo/ai_pmo_kernel/mixins/world_model/ai_pmo_kernel_mixins_world_model_session.py \
  backend/tests/test_project_operating_context_contract.py \
  backend/tests/test_ai_pmo_ingress_mainline.py
git commit -m "feat: add unified project operating context"

Task 3: Replace Hard Split Mode Guessing With A Unified Execution Recipe

Files:

  • Modify: backend/app/services/adaptive_rag_service/mixin_query_route_gateway.py

  • Modify: backend/app/services/adaptive_rag_service/mixin_core_intent.py

  • Modify: backend/app/services/adaptive_rag_service/mixin_answer_mainline.py

  • Modify: backend/app/services/adaptive_rag_service/mixin_followup_anchor.py

  • Modify: backend/tests/test_adaptive_rag_mainline_contract.py

  • Modify: backend/tests/test_rag_route_contract.py

  • Step 1: Write the failing tests for execution_recipe

Lock a composed decision like:

assert recipe == {
    "primary_lane": "external",
    "fit_brief_required": True,
    "external_evidence_required": True,
    "plan_handoff_mode": "offer_after_answer",
}

Use a scenario like:

  • question asks “这个方案适不适合我们项目”

  • current project exists

  • no explicit draft / requirement strong anchor

  • Step 2: Extend query route gateway output from route_mode to execution_recipe

Keep route_mode / effective_source_policy / reply_contract for compatibility, but add a higher-level recipe:

{
  "primary_lane": "internal" | "external" | "plan",
  "fit_brief_required": bool,
  "external_evidence_required": bool,
  "plan_handoff_mode": "none" | "offer_after_answer" | "explicit_plan_now",
}
  • Step 3: Make answer mainline follow the recipe instead of local mode guesses

Required behavior:

  • explicit plan anchor -> explicit_plan_now

  • pure internal question -> internal

  • pure external fact question -> external

  • “适不适合当前项目 / 推荐什么 / 要不要接” -> external + fit_brief + offer_after_answer

  • Step 4: Keep follow-up rewrite anchored to the unified packet

Follow-up should first look at:

  • explicit anchor
  • project operating context
  • prior handoff state

Only then fall back to session subject.

  • Step 5: Run the query mainline tests

Run:

cd /Users/yuuuu/Documents/AIProjectManger && PYTHONPATH=backend backend/venv/bin/python -m pytest \
  backend/tests/test_rag_route_contract.py \
  backend/tests/test_adaptive_rag_mainline_contract.py -v

Expected: PASS

  • Step 6: Commit
git add backend/app/services/adaptive_rag_service/mixin_query_route_gateway.py \
  backend/app/services/adaptive_rag_service/mixin_core_intent.py \
  backend/app/services/adaptive_rag_service/mixin_answer_mainline.py \
  backend/app/services/adaptive_rag_service/mixin_followup_anchor.py \
  backend/tests/test_adaptive_rag_mainline_contract.py \
  backend/tests/test_rag_route_contract.py
git commit -m "feat: add unified execution recipe for project ai"

Task 4: Add Project Fit Brief Without Breaking External-Only Answer Contracts

Files:

  • Create: backend/app/services/adaptive_rag_service/mixin_project_fit_brief.py

  • Modify: backend/app/services/adaptive_rag_service/service.py

  • Modify: backend/app/services/adaptive_rag_service/mixin_manifest_feature_p1.py

  • Modify: backend/app/services/project_knowledge_graph_service/mixin_codebase_overview.py

  • Modify: backend/app/services/context_cache_service/mixin_user_snapshot.py

  • Modify: backend/app/services/external_query_service/schemas.py

  • Modify: backend/app/services/external_query_service/planner.py

  • Modify: backend/app/services/external_query_service/gateway.py

  • Modify: backend/app/services/external_query_service/presenter.py

  • Modify: backend/app/services/adaptive_rag_service/mixin_answer_payload.py

  • Modify: backend/tests/test_external_query_planner.py

  • Modify: backend/tests/test_internal_query_context_packet.py

  • Modify: backend/tests/test_adaptive_rag_external_followup_contract.py

  • Step 1: Write failing tests for a customer-safe fit brief

Lock a brief such as:

brief = {
    "project_summary": "当前项目是 React + FastAPI 的项目管理系统",
    "repo_summary": "当前仓库同时包含 frontend/backend",
    "current_capabilities": ["feature_catalog", "feishu", "adaptive_rag"],
    "constraints": ["必须绑定正式业务对象", "不能做泛化聊天"],
    "recommendation_focus": ["适合当前项目", "接入成本", "对现有架构影响"],
}

Also lock the external recommendation contract:

assert payload["external_recommendation"]["retrieval_status"] in {
    "evidence_found",
    "verifier_blocked",
    "no_relevant_results",
    "retrieval_failed",
}
assert payload["external_recommendation"]["impact_status"] in {
    "direct_match",
    "indirect_only",
    "no_direct_impact",
}
  • Step 2: Implement the fit-brief builder from internal project and repo signals

Allowed inputs:

  • current project summary
  • feature catalog
  • codebase overview
  • customer-safe current user/project participation snapshot

Not allowed:

  • raw internal source labels in final external answer prompt

  • giant free-form repo dump

  • silent fallback that collapses verifier_blocked / no_relevant_results / retrieval_failed into one generic “暂无外部变化”

  • Step 3: Extend external planner to consume the brief as search guidance

The planner may use:

  • fit_brief_summary
  • project_scope_hint
  • recommendation_goal
  • formal project object anchors such as signal_sources / current capability names as query constraints

But presenter must still only consume verified external evidence and plan summary.

  • Step 3.5: Add object-binding and impact-tier rules before final external recommendation

Required behavior:

  • retrieval_status is system-owned and passed through to payload/presenter; the model must not guess it.

  • if retrieval_status != evidence_found, force:

    • impact_status=no_direct_impact
    • empty external_summary
    • empty external_impact
    • empty affected_signal_sources
    • empty external_action
  • if evidence is real but only loosely related to the project, force impact_status=indirect_only

  • only allow impact_status=direct_match when external evidence can be mapped to at least one formal current-project object, preferably a formal signal source

  • if current project has formal signal_sources, the recommendation layer should prefer binding to those sources before falling back to abstract capability labels

  • external_action must be a validation/mapping/monitoring action, never a staffing or ownership action

  • Step 3.6: Lock presenter wording for failure and indirect-only states

Web/Feishu presentation must explicitly distinguish:

  • verifier_blocked: searched candidates existed but did not pass grounding / verifier checks

  • no_relevant_results: search completed but no sufficiently relevant external evidence was found

  • retrieval_failed: external retrieval failed and no usable external evidence was produced

  • indirect_only: evidence exists but only supports broad market/background relevance, not a direct project-object match

  • Step 4: Emit fit brief as a separate payload block

The answer payload should look more like:

{
  "fit_brief": {...},
  "external_recommendation": {
    "retrieval_status": "evidence_found | verifier_blocked | no_relevant_results | retrieval_failed",
    "impact_status": "direct_match | indirect_only | no_direct_impact",
    "affected_signal_sources": [...]
  },
  "recommended_next_step": {...}
}

Do not collapse all three into one answer string. Do not collapse retrieval state, impact state, and next-step CTA into one generic prose paragraph.

  • Step 5: Run focused fit-brief and external planner tests

Run:

cd /Users/yuuuu/Documents/AIProjectManger && PYTHONPATH=backend backend/venv/bin/python -m pytest \
  backend/tests/test_external_query_planner.py \
  backend/tests/test_internal_query_context_packet.py \
  backend/tests/test_adaptive_rag_external_followup_contract.py -v

Expected: PASS

  • Step 6: Commit
git add backend/app/services/adaptive_rag_service/mixin_project_fit_brief.py \
  backend/app/services/adaptive_rag_service/service.py \
  backend/app/services/adaptive_rag_service/mixin_manifest_feature_p1.py \
  backend/app/services/project_knowledge_graph_service/mixin_codebase_overview.py \
  backend/app/services/context_cache_service/mixin_user_snapshot.py \
  backend/app/services/external_query_service/schemas.py \
  backend/app/services/external_query_service/planner.py \
  backend/app/services/external_query_service/gateway.py \
  backend/app/services/external_query_service/presenter.py \
  backend/app/services/adaptive_rag_service/mixin_answer_payload.py \
  backend/tests/test_external_query_planner.py \
  backend/tests/test_internal_query_context_packet.py \
  backend/tests/test_adaptive_rag_external_followup_contract.py
git commit -m "feat: add project fit brief for external recommendation"

Task 5: Implement Default A Handoff In Web And Feishu

Files:

  • Modify: backend/app/services/adaptive_rag_service/mixin_answer_payload.py

  • Modify: backend/app/services/ai_pmo/ai_pmo_kernel_ingress_flow/mixin_entry.py

  • Modify: backend/app/services/integrations/feishu_signal_pipeline/mixin_process_message_default.py

  • Modify: backend/app/services/integrations/feishu_signal_pipeline/mixin_context_prefill_a.py

  • Modify: backend/tests/test_ai_pmo_qa_http_mainline_e2e.py

  • Modify: backend/tests/test_feishu_pipeline_queue_contract.py

  • Modify: frontend/src/components/ai-steward/FloatingQAChat.tsx

  • Modify: frontend/src/components/ai-steward/QAConversationStatusBar.tsx

  • Modify: frontend/src/components/ai-steward/QAResponseMetaCard.tsx

  • Create: frontend/src/components/ai-steward/QAResponseMetaCard.test.tsx

  • Modify: frontend/src/components/ai-steward/FloatingQAChat.test.tsx

  • Step 1: Write failing end-to-end tests for the default A behavior

Lock a case where the user asks:

这个方案适不适合我们当前项目?

Expected response contract:

  • first returns a judgment

  • includes fit brief summary

  • includes external evidence summary

  • includes one clear “是否进入 plan” next step

  • does not directly jump into requirement-plan

  • if external recommendation is verifier_blocked / no_relevant_results / retrieval_failed / indirect_only, the UI copy must preserve that distinction instead of rendering a single generic empty-state sentence

  • Step 2: Add payload fields for handoff and next-step CTA

Minimum fields:

{
  "recommended_next_step": {
    "mode": "offer_plan",
    "label": "基于这个结论进入计划",
    "allowed": true
  }
}
  • Step 3: Render the same structure in web and Feishu

Web should show three regions:

  • 当前项目画像
  • 外部推荐与证据
  • 下一步

Feishu should keep a simpler rendering:

  • 先给结论
  • 再给一行“如果你要,我可以基于这个结论继续帮你出计划”

Feishu 验收时不要只看内部 handoff payload;必须以“后端最终准备发给飞书的格式”为准:

  • qa_direct 文本:看 FeishuReceiveRoutingMixin._build_feishu_qa_direct_reply_text()

  • requirement_plan_followup / draft_pending_confirm / pending_manual:看 FeishuCardActionMixin._build_action_card_from_capture_result()

  • Step 3.5: Render explicit external state variants instead of a single empty fallback

Web must distinguish at least:

  • evidence_found + direct_match
  • evidence_found + indirect_only
  • verifier_blocked
  • no_relevant_results
  • retrieval_failed

Feishu may be shorter, but must still avoid collapsing the above states into one generic “暂无外部变化/暂无推荐”.

  • Step 4: Keep explicit plan anchors stronger than the default A fallback

If the message already contains a strong plan anchor:

  • skip the offer
  • enter plan immediately

This prevents regression for active requirement / draft threads.

  • Step 5: Run web and Feishu integration tests

Run:

cd /Users/yuuuu/Documents/AIProjectManger && PYTHONPATH=backend backend/venv/bin/python -m pytest \
  backend/tests/test_ai_pmo_qa_http_mainline_e2e.py \
  backend/tests/test_feishu_pipeline_queue_contract.py -v

cd /Users/yuuuu/Documents/AIProjectManger/frontend && npx vitest run \
  src/components/ai-steward/FloatingQAChat.test.tsx \
  src/components/ai-steward/QAResponseMetaCard.test.tsx

Expected: PASS

  • Step 6: Commit
git add backend/app/services/adaptive_rag_service/mixin_answer_payload.py \
  backend/app/services/ai_pmo/ai_pmo_kernel_ingress_flow/mixin_entry.py \
  backend/app/services/integrations/feishu_signal_pipeline/mixin_process_message_default.py \
  backend/app/services/integrations/feishu_signal_pipeline/mixin_context_prefill_a.py \
  backend/tests/test_ai_pmo_qa_http_mainline_e2e.py \
  backend/tests/test_feishu_pipeline_queue_contract.py \
  frontend/src/components/ai-steward/FloatingQAChat.tsx \
  frontend/src/components/ai-steward/QAConversationStatusBar.tsx \
  frontend/src/components/ai-steward/QAResponseMetaCard.tsx \
  frontend/src/components/ai-steward/QAResponseMetaCard.test.tsx \
  frontend/src/components/ai-steward/FloatingQAChat.test.tsx
git commit -m "feat: add unified plan handoff for project ai answers"

Task 6: Update Docs And Run Final Regression

Files:

  • Modify: CLAUDE.md

  • Modify: docs/architecture/ARCHITECTURE_INVENTORY.md

  • Modify: docs/architecture/CODE_REFERENCE_INDEX.md

  • Modify: docs/architecture/RUNTIME_GUARDRAILS.md

  • Step 1: Update runtime guardrails

Document these new stable rules:

  • unified Project Operating Context

  • default scope is current project + current repo

  • default multi-intent strategy is answer_then_offer_plan

  • external planner may consume fit brief, but external presenter remains external-only

  • Step 2: Update inventory and code reference docs

Add:

  • new schema/module paths

  • ingress builder path

  • fit brief builder path

  • handoff payload path

  • external recommendation retrieval/impact state contract

  • explicit object-binding preference for formal signal_sources

  • Step 3: Run backend compile, tests, and deployment restart

Run:

cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m py_compile \
  backend/app/schemas/project_operating_context.py \
  backend/app/services/ai_pmo/project_operating_context_builder.py \
  backend/app/services/adaptive_rag_service/*.py \
  backend/app/services/external_query_service/*.py

cd /Users/yuuuu/Documents/AIProjectManger && PYTHONPATH=backend backend/venv/bin/python -m pytest \
  backend/tests/test_project_operating_context_contract.py \
  backend/tests/test_ai_pmo_ingress_mainline.py \
  backend/tests/test_rag_route_contract.py \
  backend/tests/test_adaptive_rag_mainline_contract.py \
  backend/tests/test_adaptive_rag_external_followup_contract.py \
  backend/tests/test_external_query_planner.py \
  backend/tests/test_internal_query_context_packet.py \
  backend/tests/test_ai_pmo_qa_http_mainline_e2e.py \
  backend/tests/test_feishu_pipeline_queue_contract.py -v

cd /Users/yuuuu/Documents/AIProjectManger/frontend && npx tsc --noEmit
cd /Users/yuuuu/Documents/AIProjectManger/frontend && npm run build
cd /Users/yuuuu/Documents/AIProjectManger && docker compose up -d --build backend

Expected:

  • py_compile success

  • targeted pytest all PASS

  • tsc --noEmit success

  • npm run build success

  • backend container returns healthy

  • Step 3.5: Add runtime feature flags / rollback guards

At minimum, the implementation should support disabling independently:

  • unified execution recipe
  • fit-brief-driven external recommendation
  • default answer_then_offer_plan handoff rendering

The rollback path must not require reverting unrelated ingress/context changes.

  • Step 3.6: Run real-chain verification on at least three external recommendation states

Before final sign-off, capture real examples for:

  • verifier_blocked
  • evidence_found + indirect_only
  • evidence_found + direct_match

For each example, confirm:

  • external payload state is correct

  • presenter wording matches the state

  • no generic empty fallback sentence hides the real reason

  • direct_match only appears when a formal project object is actually bound

  • Step 4: Commit docs and verification updates

git add CLAUDE.md \
  docs/architecture/ARCHITECTURE_INVENTORY.md \
  docs/architecture/CODE_REFERENCE_INDEX.md \
  docs/architecture/RUNTIME_GUARDRAILS.md
git commit -m "docs: document unified project ai context and handoff"

Final Verification Checklist

  • Project Operating Context is present in ingress before any route or plan handoff
  • external recommendation can consume internal fit brief without mixing internal evidence into final external answer
  • external recommendation explicitly separates retrieval state from impact state
  • external recommendation prefers binding to formal current-project objects, especially signal_sources
  • direct_match is only used when external evidence truly maps to a formal project object
  • indirect_only is used for broad market/background relevance instead of pretending the recommendation is project-bound
  • external actions never include staffing/ownership changes or direct business-status mutation
  • multi-intent recommendation questions default to “answer first, then offer plan”
  • strong plan anchors still override the offer and enter plan directly
  • web and Feishu both display the same handoff logic
  • no new runtime keyword/regex route-takeover helpers were introduced
  • implementation diff does not reintroduce hard semantic routing under another name

Execution Notes

  • Task order matters:
    • Task 1 and Task 2 first, otherwise later tasks will keep smuggling context through ad-hoc fields
    • Task 3 before Task 4, otherwise fit brief will bolt onto the old route split
    • Task 5 only after the recipe and fit-brief payload exist
  • Keep commits small and reviewable; do not batch all six tasks into one mega-commit.
  • If the implementation reveals that full repo understanding needs a separate ingestion pipeline beyond current codebase snapshot, split that work out as a follow-up plan instead of bloating this one.

Final Architecture Baseline

This plan must land on the following stable architecture baseline and should not drift after implementation starts.

  1. PMFlow must end up with one unified project AI ingress, not three isolated user-facing modes.
  2. The first runtime layer is always Project Operating Context. Default scope is fixed to:
    • current project
    • current repo summary when runtime snapshot is available
    • explicit thread / page / reply anchors
  3. After the top-level plan_mode gate, non-plan recommendation / query flows should produce a single execution_recipe. That recipe layers on top of the existing route_mode / effective_source_policy / reply_contract / probe_snapshot contract and decides:
    • internal execution
    • external execution
    • post-answer handoff to plan_mode downstream capability
  4. internal is responsible for:
    • internal object facts
    • project state
    • project fit brief
    • bounded compatibility / capability constraints
  5. external is responsible for:
    • external evidence
    • grounded recommendations
    • impact assessment
    • explicit retrieval / verifier state
    • customer-safe presentation on top of external-only evidence
  6. Here planmode means the repo's plan_mode gate plus requirement_plan / draft_pending_confirm mainline, not a separate free-floating route name.
  7. planmode is a downstream capability for recommendation questions, not the default first response. Default strategy remains:
    • answer first
    • then offer plan handoff
  8. External recommendation may consume internal fit brief, but the final external answer must remain external-only and single-source-policy compliant.
  9. Strong anchors override the default answer-then-offer-plan strategy:
    • explicit reply/thread
    • explicit work item
    • active requirement/draft context
  10. Feishu is a first-class acceptance surface. The work is not complete until the Feishu problem inventory and golden cases pass.
  11. The implementation is forbidden from reintroducing hard semantic routing:
  • no keyword warehouse
  • no regex routing helpers
  • no “looks like this kind of question” split logic in execution code

Target end-state in one sentence:

Understand the current PMFlow project first, then decide how internal evidence, external evidence, and plan handoff should cooperate under one ingress.