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

14 KiB
Raw Blame History

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:

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:

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:

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:

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:

"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:

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:

async getQASessionState(sessionId: number): Promise<QASessionStateReplay>
  • Step 4: Re-run the focused backend tests

Run:

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:

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:

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:

<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:

<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:

onChooseInternal={() => runQuestionRequest({ prompt: "B" })}
onChooseExternal={() => runQuestionRequest({ prompt: "A" })}
  • Step 6: Re-run the front-end test

Run:

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:

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:

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:

"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:

<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:

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:

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:

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:

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