PMFlow/docs/superpowers/plans/2026-04-17-three-mode-inter...

373 lines
14 KiB
Markdown
Raw 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.

# Three-Mode Interaction And Context Upgrade 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:** 让 PMFlow 的三类用户可见模式在同一套交互骨架下可见、可控、可纠偏:`planmode(requirement_plan/draft)` 先做顶层状态显化,`query.answer` 再按 `internal / external` 给出显式来源合同与上下文续写提示。
**Architecture:** 先锁共享基线,不改语义边界:保留 `plan_mode gate -> non-plan query.answer -> internal/external split` 的现有层级,只把后端已经存在的 `route_decision / routing_snapshot / session_state / requirement plan status` 暴露给前端。前端新增一套轻量状态条和回答元信息卡,查询侧挂在 `FloatingQAChat`,planmode 侧挂在 `Inbox` 与 AI PMO handoff 结果,不另起一套聊天壳。
**Tech Stack:** FastAPI, SQLAlchemy 2.0, PMFlow AdaptiveRAG, AI PMO kernel, React 18, TypeScript, Vitest, pytest
---
## Baseline File Map
### Shared Query Contract
- Modify: `backend/app/api/rag_ask_contract.py`
- 保持 `/rag/ask` 现有包络不变,补齐给前台可消费的 `route_decision / clarification_code / clarification_candidates / answer_mode / grounding_status / policy_version / citations / sources_used / evidence_pack` 规范化输出。
- Modify: `backend/app/services/adaptive_rag_service/mixin_qa_session_list_ops_p1.py`
- 让会话详情把每轮回答的 route / evidence / answer mode 元信息带出来,不再只有 `question / answer / sources`。
- Modify: `frontend/src/api/aiSteward.ts`
- 前端类型补齐 richer QA payload,并新增 `getQASessionState(sessionId)` 读取 `last_subject / pending_clarification / rewrite_history`。
- Modify: `backend/tests/test_ai_pmo_qa_http_mainline_e2e.py`
- 锁 `/rag/ask` HTTP 响应合同。
- Modify: `backend/tests/test_ai_pmo_ingress_mainline.py`
- 锁 QA session detail / state replay 合同。
### Query UI Shell
- Create: `frontend/src/components/ai-steward/QAConversationStatusBar.tsx`
- 显示当前对象、当前来源模式、待澄清状态、是否仍在继承上一轮上下文。
- Create: `frontend/src/components/ai-steward/QAResponseMetaCard.tsx`
- 每条回答下显示 `内部/外部/待澄清`、grounding 状态、来源摘要、推荐下一步。
- Modify: `frontend/src/components/ai-steward/FloatingQAChat.tsx`
- 接入 status bar、message-level meta card、route clarify 快捷动作、session state 拉取与“清空上下文/新话题”入口。
- Create: `frontend/src/components/ai-steward/FloatingQAChat.test.tsx`
- 锁状态条、route clarify 动作、元信息卡渲染。
### Plan Mode Surface
- Modify: `backend/app/api/ai_pmo_api/helpers_inbox_payload.py`
- 给 inbox work item summary 增加 planmode 相关轻量字段,例如 `plan_mode / plan_status / requirement_plan_context_summary`。
- Modify: `backend/app/api/ai_pmo_api/routes_core.py`
- 统一 `process-text` 与 `work-items` 输出里的 planmode 相关字段,避免前端靠 `reply_template_key` 猜。
- Modify: `frontend/src/api/aiPmo.ts`
- 补齐 AI PMO result / work item summary 的 planmode 类型。
- Modify: `frontend/src/types/inbox.ts`
- 让 inbox item view model 能承载 `plan_status / plan_mode / requirement_plan_context_summary`。
- Modify: `frontend/src/components/inbox/InboxListItem.tsx`
- 在详情卡里显式展示“当前需求模式状态 / 下一步动作 / 草稿待确认”。
- Modify: `frontend/src/pages/inbox/InboxPage.tsx`
- 把 mounted run / work item 映射到统一的 planmode 视图文案。
- Create: `frontend/src/components/inbox/InboxListItem.test.tsx`
- 锁 planmode 状态渲染与 CTA 文案。
### Docs And Inventory
- Modify: `CLAUDE.md`
- Modify: `docs/architecture/ARCHITECTURE_INVENTORY.md`
- Modify: `docs/architecture/CODE_REFERENCE_INDEX.md`
- Modify: `docs/architecture/RUNTIME_GUARDRAILS.md`
---
### Task 1: Lock The Shared QA Contract
**Files:**
- Modify: `backend/tests/test_ai_pmo_qa_http_mainline_e2e.py`
- Modify: `backend/tests/test_ai_pmo_ingress_mainline.py`
- Modify: `frontend/src/api/aiSteward.ts`
- [ ] **Step 1: Write the failing `/rag/ask` contract test**
Add an HTTP contract assertion that `/rag/ask` returns:
```python
assert payload["data"]["route_decision"]["effective_source_policy"] == "internal"
assert payload["data"]["clarification_code"] is None
assert payload["data"]["answer_mode"] == "fast_path"
assert payload["data"]["evidence_pack"]["summary"]["conclusion"] == "当前主链状态正常。"
```
- [ ] **Step 2: Write the failing session detail/state test**
Add sqlite-backed tests that prove:
```python
detail = await service.get_qa_session_detail(session_id=session_id, user_id=101)
assert detail["messages"][0]["route_decision"]["effective_source_policy"] == "external"
assert detail["messages"][0]["grounding_status"] == "insufficient_evidence"
state = await service.get_session_state_replay(session_id=session_id, user_id=101)
assert state["last_subject"] == "Alpha项目"
assert state["pending_clarification"]["kind"] == "query_route_selection"
```
- [ ] **Step 3: Run the focused backend tests to confirm they fail**
Run:
```bash
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m pytest backend/tests/test_ai_pmo_qa_http_mainline_e2e.py -k "rag_ask" -v
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m pytest backend/tests/test_ai_pmo_ingress_mainline.py -k "qa_session_detail or session_state_replay" -v
```
Expected: FAIL because the richer QA/session contract is not exposed yet.
### Task 2: Implement The Shared QA Metadata Baseline
**Files:**
- Modify: `backend/app/api/rag_ask_contract.py`
- Modify: `backend/app/services/adaptive_rag_service/mixin_qa_session_list_ops_p1.py`
- Modify: `frontend/src/api/aiSteward.ts`
- Test: `backend/tests/test_ai_pmo_qa_http_mainline_e2e.py`
- Test: `backend/tests/test_ai_pmo_ingress_mainline.py`
- [ ] **Step 1: Normalize `/rag/ask` payload fields**
Keep the existing response envelope, but explicitly preserve:
```python
payload["route_decision"] = dict(payload.get("route_decision") or {})
payload["clarification_code"] = str(payload.get("clarification_code") or "").strip() or None
payload["clarification_candidates"] = _normalize_dict_list(payload.get("clarification_candidates"))
```
- [ ] **Step 2: Extend QA session detail payload**
When serializing session messages, add:
```python
"route_decision": row.routing_snapshot.get("query_route") if isinstance(row.routing_snapshot, dict) else {},
"answer_mode": row.answer_mode,
"grounding_status": row.grounding_status,
"policy_version": row.policy_version,
"citations": row.citations or [],
"sources_used": row.sources_used or [],
"evidence_pack": row.evidence_pack or {},
```
- [ ] **Step 3: Update front-end request/response types**
Extend `QAAskResponse` and `QASessionMessage` to accept:
```ts
route_decision?: Record<string, unknown> | null;
clarification_code?: string | null;
clarification_candidates?: Array<Record<string, unknown>>;
answer_mode?: string | null;
grounding_status?: string | null;
policy_version?: string | null;
citations?: Array<Record<string, unknown>>;
sources_used?: Array<Record<string, unknown>>;
evidence_pack?: Record<string, unknown> | null;
```
Also add:
```ts
async getQASessionState(sessionId: number): Promise<QASessionStateReplay>
```
- [ ] **Step 4: Re-run the focused backend tests**
Run:
```bash
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m pytest backend/tests/test_ai_pmo_qa_http_mainline_e2e.py -k "rag_ask" -v
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m pytest backend/tests/test_ai_pmo_ingress_mainline.py -k "qa_session_detail or session_state_replay" -v
```
Expected: PASS
### Task 3: Build The Query Status Shell
**Files:**
- Create: `frontend/src/components/ai-steward/QAConversationStatusBar.tsx`
- Create: `frontend/src/components/ai-steward/QAResponseMetaCard.tsx`
- Modify: `frontend/src/components/ai-steward/FloatingQAChat.tsx`
- Create: `frontend/src/components/ai-steward/FloatingQAChat.test.tsx`
- Test: `frontend/src/components/ai-steward/FloatingQAChat.test.tsx`
- [ ] **Step 1: Write the failing front-end tests**
Cover these cases:
```tsx
it("shows current subject and source mode in the status bar", ...)
it("renders route clarify actions when clarification_code=query_route_clarify", ...)
it("renders answer meta card with grounding and source summary", ...)
```
- [ ] **Step 2: Run the front-end test to verify it fails**
Run:
```bash
cd /Users/yuuuu/Documents/AIProjectManger/frontend && npx vitest run src/components/ai-steward/FloatingQAChat.test.tsx
```
Expected: FAIL because the new status shell does not exist yet.
- [ ] **Step 3: Implement the session-level status bar**
Render a compact bar above the message list with:
```tsx
<QAConversationStatusBar
currentSubject={sessionState.last_subject}
effectiveSourcePolicy={lastRouteDecision?.effective_source_policy}
pendingClarification={sessionState.pending_clarification}
onClearContext={startNewChat}
/>
```
- [ ] **Step 4: Implement the per-answer meta card**
For each message, render a compact card that surfaces:
```tsx
<QAResponseMetaCard
routeDecision={msg.route_decision}
groundingStatus={msg.grounding_status}
sourcesUsed={msg.sources_used}
evidencePack={msg.evidence_pack}
/>
```
- [ ] **Step 5: Wire route clarify actions**
When the last answer returns `clarification_code === "query_route_clarify"`, show two quick actions that submit the existing protocol text:
```tsx
onChooseInternal={() => runQuestionRequest({ prompt: "B" })}
onChooseExternal={() => runQuestionRequest({ prompt: "A" })}
```
- [ ] **Step 6: Re-run the front-end test**
Run:
```bash
cd /Users/yuuuu/Documents/AIProjectManger/frontend && npx vitest run src/components/ai-steward/FloatingQAChat.test.tsx
```
Expected: PASS
### Task 4: Surface Plan Mode In AI PMO And Inbox
**Files:**
- Modify: `backend/tests/test_ai_pmo_ingress_mainline.py`
- Modify: `backend/app/api/ai_pmo_api/helpers_inbox_payload.py`
- Modify: `backend/app/api/ai_pmo_api/routes_core.py`
- Modify: `frontend/src/api/aiPmo.ts`
- Modify: `frontend/src/types/inbox.ts`
- Modify: `frontend/src/components/inbox/InboxListItem.tsx`
- Modify: `frontend/src/pages/inbox/InboxPage.tsx`
- Create: `frontend/src/components/inbox/InboxListItem.test.tsx`
- Test: `frontend/src/components/inbox/InboxListItem.test.tsx`
- [ ] **Step 1: Write the failing backend test for planmode summary fields**
Add coverage that proves work item summary/detail exposes:
```python
assert item["plan_mode"] is True
assert item["plan_status"] == "collecting"
assert item["requirement_plan_context_summary"]["title"] == "换季搭配推荐"
```
- [ ] **Step 2: Run the targeted backend test to verify it fails**
Run:
```bash
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m pytest backend/tests/test_ai_pmo_ingress_mainline.py -k "plan_mode or requirement_plan_context_summary" -v
```
Expected: FAIL because inbox/process-text payloads do not expose these fields yet.
- [ ] **Step 3: Serialize planmode summary fields in AI PMO payloads**
Normalize a small summary shape instead of leaking full runtime packet:
```python
"plan_mode": bool(summary.get("plan_mode")),
"plan_status": normalized_plan_status,
"requirement_plan_context_summary": {
"title": card.get("title"),
"goal": card.get("goal"),
"missing_info": card.get("missing_info", [])[:3],
}
```
- [ ] **Step 4: Update front-end inbox types and rendering**
Show:
```tsx
<Badge variant="outline">{item.source_work_item?.plan_status || "draft_pending_confirm"}</Badge>
<div>当前模式:需求模式</div>
<div>下一步:补信息 / 选 A-B-C / 草稿确认</div>
```
Keep the existing draft actions intact.
- [ ] **Step 5: Write and run the front-end inbox test**
Run:
```bash
cd /Users/yuuuu/Documents/AIProjectManger/frontend && npx vitest run src/components/inbox/InboxListItem.test.tsx
```
Expected: FAIL first, then PASS after the new planmode summary UI is wired.
### Task 5: Verification And Docs Sync
**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 docs for the new three-mode front-end contract**
Record:
- `plan_mode` is still the top-level gate
- `internal / external` remain submodes under `query.answer`
- `FloatingQAChat` now shows context/source status instead of only answer text
- inbox exposes requirement-plan summary state
- [ ] **Step 2: Run focused backend verification**
Run:
```bash
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m pytest \
backend/tests/test_ai_pmo_qa_http_mainline_e2e.py \
backend/tests/test_ai_pmo_ingress_mainline.py \
backend/tests/test_adaptive_rag_mainline_contract.py -v
```
Expected: PASS
- [ ] **Step 3: Run backend compile verification**
Run:
```bash
cd /Users/yuuuu/Documents/AIProjectManger && backend/venv/bin/python -m py_compile \
backend/app/api/rag_ask_contract.py \
backend/app/services/adaptive_rag_service/mixin_qa_session_list_ops_p1.py \
backend/app/api/ai_pmo_api/helpers_inbox_payload.py \
backend/app/api/ai_pmo_api/routes_core.py
```
Expected: exit 0
- [ ] **Step 4: Run front-end verification**
Run:
```bash
cd /Users/yuuuu/Documents/AIProjectManger/frontend && npx vitest run \
src/components/ai-steward/FloatingQAChat.test.tsx \
src/components/inbox/InboxListItem.test.tsx
cd /Users/yuuuu/Documents/AIProjectManger/frontend && npx tsc --noEmit
cd /Users/yuuuu/Documents/AIProjectManger/frontend && npm run build
```
Expected: PASS