39 KiB
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_modegate 的 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_foundverifier_blockedno_relevant_resultsretrieval_failed
- 下面这些
impact/action约束属于这份计划的 v1 product guardrail,不应被误读成 repo 现状已经完整落地的 runtime contract;实现时若与现有对象模型冲突,应先回到正式业务对象和 guardrail 真源校正。 - external recommendation 的“影响判断”必须独立于“检索状态”表达:
direct_matchindirect_onlyno_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太粗。
- 持久化最小必要的 project operating context snapshot,避免 session 只记
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。
- 负责从项目对象、功能目录、repo 概览、当前能力缺口生成 customer-safe
- 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等检索约束字段。
- 给 external plan / execution 增加
- Modify:
backend/app/services/external_query_service/planner.py- 让 planner 可以吃
fit brief,生成更贴项目的 query plan。
- 让 planner 可以吃
- Modify:
backend/app/services/external_query_service/gateway.py- 保留 adapter/verifier 主链,同时把
fit_brief作为 planner meta 一部分审计。
- 保留 adapter/verifier 主链,同时把
- 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。
- 在回答 payload 里增加
- 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/askreceives 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_idcurrent_repo_scopelast_page_anchorlast_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_modetoexecution_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_failedinto one generic “暂无外部变化” -
Step 3: Extend external planner to consume the brief as search guidance
The planner may use:
fit_brief_summaryproject_scope_hintrecommendation_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_statusis 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_matchwhen external evidence can be mapped to at least one formal current-project object, preferably a formalsignal 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_actionmust 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_matchevidence_found + indirect_onlyverifier_blockedno_relevant_resultsretrieval_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_compilesuccess -
targeted pytest all PASS
-
tsc --noEmitsuccess -
npm run buildsuccess -
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_planhandoff 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_blockedevidence_found + indirect_onlyevidence_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_matchonly 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 Contextis 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_matchis only used when external evidence truly maps to a formal project objectindirect_onlyis 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.
- PMFlow must end up with
one unified project AI ingress, not three isolated user-facing modes. - 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
- After the top-level
plan_modegate, non-plan recommendation / query flows should produce a singleexecution_recipe. That recipe layers on top of the existingroute_mode / effective_source_policy / reply_contract / probe_snapshotcontract and decides:- internal execution
- external execution
- post-answer handoff to
plan_modedownstream capability
internalis responsible for:- internal object facts
- project state
- project fit brief
- bounded compatibility / capability constraints
externalis responsible for:- external evidence
- grounded recommendations
- impact assessment
- explicit retrieval / verifier state
- customer-safe presentation on top of external-only evidence
- Here
planmodemeans the repo'splan_modegate plusrequirement_plan / draft_pending_confirmmainline, not a separate free-floating route name. planmodeis a downstream capability for recommendation questions, not the default first response. Default strategy remains:- answer first
- then offer plan handoff
- External recommendation may consume internal
fit brief, but the final external answer must remain external-only and single-source-policy compliant. - Strong anchors override the default answer-then-offer-plan strategy:
- explicit reply/thread
- explicit work item
- active requirement/draft context
- Feishu is a first-class acceptance surface. The work is not complete until the Feishu problem inventory and golden cases pass.
- 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.