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

884 lines
39 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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:
```text
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:
```markdown
- 场景名
- 初始上下文
- 用户原话
- 当前错误行为
- 目标行为
- 应命中的 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:
```text
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:
```bash
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:
```python
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:
```python
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:
```python
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:
```bash
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:
```python
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:
```bash
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**
```bash
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:
```python
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:
```python
{
"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:
```bash
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**
```bash
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:
```python
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:
```python
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:
```json
{
"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:
```bash
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**
```bash
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:
```text
这个方案适不适合我们当前项目?
```
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:
```json
{
"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:
```bash
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**
```bash
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:
```bash
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**
```bash
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.`