docs: 清理过期计划文档并补全 work-log 与开发/用户文档

- 删除已落地的 improvement-roadmap、superpowers 计划与 ui-mockups 参考稿,删除早期 work-log(2026-05-06~08)
- 新增 2026-05-23 起的 work-log 与 attachment-association-background-job、reimbursement-draft-action-branching 等开发文档及用户文档
- docker-compose(.full).yml 微调服务配置
This commit is contained in:
caoxiaozhu
2026-06-24 10:42:57 +08:00
parent ee730aa31c
commit f9553a6a1a
58 changed files with 1551 additions and 1404 deletions

View File

@@ -1,177 +0,0 @@
# Steward Application Reimbursement State 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:** Build a persistent ontology-bound steward state for travel application and travel reimbursement flows.
**Architecture:** Keep the existing steward planning UI and assistant delegation flow. Add a backend state layer that stores `steward_state` in `AgentConversation.state_json`, merges LLM/rule output as patches, and rejects fields outside the ontology registry before downstream services consume them.
**Tech Stack:** FastAPI, SQLAlchemy JSON state, Pydantic schemas, pytest in Docker `x-financial-main:/app`, Vue/Vite frontend.
---
### Task 1: Backend State Contract
**Files:**
- Modify: `server/src/app/schemas/steward.py`
- Create: `server/src/app/services/steward_flow_state.py`
- Test: `server/tests/test_steward_flow_state.py`
- [ ] **Step 1: Write failing tests**
```python
def test_state_merge_keeps_application_and_reimbursement_flows():
service = StewardFlowStateService()
state = service.merge_state(
{},
StewardFlowStatePatch(
active_flow="travel_application",
flow_id="travel_application",
intent="travel_application_create",
fields={"expense_type": "travel", "location": "上海"},
),
)
state = service.merge_state(
state,
StewardFlowStatePatch(
active_flow="travel_reimbursement",
flow_id="travel_reimbursement",
intent="travel_reimbursement_draft",
fields={"amount": "708", "invoice_no": "NO-1"},
),
)
assert state["flows"]["travel_application"]["fields"]["location"] == "上海"
assert state["flows"]["travel_reimbursement"]["fields"]["amount"] == "708"
```
```python
def test_state_merge_filters_non_ontology_fields():
service = StewardFlowStateService()
state = service.merge_state(
{},
StewardFlowStatePatch(
active_flow="travel_application",
flow_id="travel_application",
intent="travel_application_create",
fields={"location": "上海", "invented_field": "x"},
),
)
assert state["flows"]["travel_application"]["fields"] == {"location": "上海"}
```
- [ ] **Step 2: Run red tests**
Run:
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_steward_flow_state.py
```
Expected: fail because `steward_flow_state.py` does not exist.
- [ ] **Step 3: Implement minimal state service**
Create `StewardFlowStatePatch`, `StewardFlowStateService.merge_state`, ontology field filtering, and event append logic.
- [ ] **Step 4: Run green tests**
Run the same pytest command and expect pass.
### Task 2: Steward Plan Persistence
**Files:**
- Modify: `server/src/app/schemas/steward.py`
- Modify: `server/src/app/api/v1/endpoints/steward.py`
- Modify: `server/src/app/services/agent_conversations.py`
- Test: `server/tests/test_steward_planner.py`
- [ ] **Step 1: Write failing API/service test**
Add a test proving `/steward/plans` response contains `conversation_id` and `steward_state` when `context_json.session_type = steward`, and the state contains two flows when the input contains one application and one reimbursement task.
- [ ] **Step 2: Run red test**
Run:
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_steward_planner.py
```
- [ ] **Step 3: Implement conversation state persistence**
Add `conversation_id` and `steward_state` fields to response schemas, persist state through `AgentConversationService`, and merge planner tasks into `steward_state`.
- [ ] **Step 4: Run green test**
Run the same pytest command and expect pass.
### Task 3: Runtime Decision Reads Persistent State
**Files:**
- Modify: `server/src/app/services/steward_runtime_decision_agent.py`
- Test: `server/tests/test_steward_runtime_decision_agent.py`
- [ ] **Step 1: Write failing test**
Add a test proving runtime decision uses `context_json.conversation_state.steward_state` when `runtime_state` is empty.
- [ ] **Step 2: Implement minimal fallback hydration**
Normalize runtime state by merging request runtime state with persisted steward state.
- [ ] **Step 3: Run green test**
Run:
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_steward_runtime_decision_agent.py
```
### Task 4: Frontend State Carry
**Files:**
- Modify: `web/src/views/scripts/stewardPlanModel.js`
- Modify: `web/src/views/scripts/TravelReimbursementCreateView.js`
- Modify: `web/src/views/scripts/useTravelReimbursementSessionState.js`
- [ ] **Step 1: Preserve steward state from backend responses**
Normalize `conversation_id` and `steward_state` from plan/runtime responses into the local session model.
- [ ] **Step 2: Send steward state in later requests**
Include the current `steward_state` under `context_json` for plan and runtime decision calls.
- [ ] **Step 3: Build frontend**
Run:
```bash
docker exec -w /app/web x-financial-main npm run build
```
Expected: build succeeds.
### Task 5: Final Verification
- [ ] **Step 1: Run backend steward tests**
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_steward_flow_state.py server/tests/test_steward_planner.py server/tests/test_steward_runtime_decision_agent.py server/tests/test_steward_slot_decision_agent.py
```
- [ ] **Step 2: Run frontend build**
```bash
docker exec -w /app/web x-financial-main npm run build
```
- [ ] **Step 3: Report workspace status**
Run:
```bash
git status --short
```

View File

@@ -1,159 +0,0 @@
# Refactor Under 800 Lines 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:** Keep every Python class and every frontend core component/module under 800 lines, while deleting proven dead code and reducing avoidable runtime overhead.
**Architecture:** Add automated code-size guardrails first, then split oversized units by existing responsibility boundaries. Preserve public APIs wherever possible, move private helpers into focused modules, and delete code only after usage checks or tests prove it is not needed.
**Tech Stack:** Vue single-file components, Vite/Node test runner, Python/FastAPI service layer, pytest inside Docker container `x-financial-main`.
---
### Task 1: Guardrails
**Files:**
- Create: `web/tests/code-size-limits.test.mjs`
- Create: `server/tests/test_code_size_limits.py`
- [x] **Step 1: Add frontend source-unit limit test**
```bash
node --test web/tests/code-size-limits.test.mjs
```
Expected current result: FAIL, listing oversized files in `web/src/components`, `web/src/composables`, `web/src/utils`, and `web/src/views`.
- [x] **Step 2: Add backend class limit test**
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_code_size_limits.py
```
Expected current result: FAIL, listing Python classes over 800 lines.
### Task 2: Backend Python Class Split
**Files:**
- Modify: `server/src/app/services/user_agent_application.py`
- Modify: `server/src/app/services/risk_rule_template_executor.py`
- Modify: `server/src/app/services/steward_planner.py`
- Modify: `server/src/app/services/receipt_folder.py`
- Modify: `server/src/app/services/expense_claim_draft_flow.py`
- Modify: `server/src/app/services/expense_claims.py`
- Modify: `server/src/app/services/orchestrator_execution.py`
- Modify: `server/src/app/services/finance_dashboard.py`
- Modify: `server/src/app/services/agent_assets.py`
- Create focused helper or mixin files under `server/src/app/services/`
- [ ] **Step 1: Split low-risk helper groups first**
Move private formatting, parsing, label, and serialization methods into helper mixins or helper modules. Keep original public classes and method names stable.
- [ ] **Step 2: Split domain-heavy groups**
Move larger responsibility groups into named mixins:
```text
UserAgentApplicationMixin
├── application fact resolution
├── application persistence
└── application duplicate/detail helpers
RiskRuleTemplateExecutor
├── condition evaluators
├── value resolvers
└── date/window parsing
ReceiptFolderService
├── storage/meta helpers
├── editable field resolution
└── train ticket extraction
```
- [ ] **Step 3: Verify backend class limit**
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_code_size_limits.py
```
Expected final result: PASS.
- [ ] **Step 4: Run targeted backend regression tests**
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_user_agent_service.py server/tests/test_expense_claim_service.py server/tests/test_steward_planner.py server/tests/test_risk_rule_generation.py server/tests/test_agent_asset_service.py server/tests/test_reimbursement_endpoints.py
```
Expected final result: PASS or a documented pre-existing failure with evidence.
### Task 3: Frontend Source Unit Split
**Files:**
- Modify: `web/src/components/business/PersonalWorkbenchAiMode.vue`
- Modify: `web/src/views/scripts/TravelReimbursementCreateView.js`
- Modify: `web/src/views/scripts/TravelRequestDetailView.js`
- Modify: `web/src/views/scripts/useTravelReimbursementSubmitComposer.js`
- Modify remaining files reported by `web/tests/code-size-limits.test.mjs`
- Create focused modules beside the existing owners under `web/src/views/scripts/`, `web/src/components/`, `web/src/composables/`, and `web/src/utils/`
- [ ] **Step 1: Split pure helpers before stateful runtime code**
Extract pure formatting, payload normalization, label mapping, row building, and text rendering helpers. This reduces file size without changing component state ownership.
- [ ] **Step 2: Split composables and child components**
For Vue files, move stable repeated UI blocks into child components only when props/events are clear. For script modules, move independent computed builders and action helpers into colocated modules.
- [ ] **Step 3: Remove proven redundant frontend code**
Use `rg` to confirm an export, class, helper, CSS hook, or branch is unused before deleting it. If a usage is dynamic, keep it unless a regression test proves it is dead.
- [ ] **Step 4: Verify frontend source limit**
```bash
node --test web/tests/code-size-limits.test.mjs
```
Expected final result: PASS.
- [ ] **Step 5: Run targeted frontend regression tests**
```bash
node --test web/tests/workbench-ai-mode-switch.test.mjs web/tests/expense-application-fast-preview.test.mjs web/tests/expense-profile-detail-modal.test.mjs web/tests/finance-dashboard-ranking.test.mjs
npm --prefix web run build
```
Expected final result: PASS.
### Task 4: Performance And Cleanup Pass
**Files:**
- Modify only files already touched by Tasks 2 and 3 unless a usage check proves a separate dead module can be removed.
- [ ] **Step 1: Remove repeated computation inside hot paths**
Cache local computed values inside functions, avoid repeated JSON/string/date parsing loops, and prefer early returns for blocked states.
- [ ] **Step 2: Delete redundant private helpers**
Delete helpers only when all of these checks are true:
```bash
rg "helperName" server web
node --test affected-web-test.mjs
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q affected_server_test.py
```
- [ ] **Step 3: Final verification**
```bash
node --test web/tests/code-size-limits.test.mjs
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_code_size_limits.py
npm --prefix web run build
```
Expected final result: PASS.

View File

@@ -0,0 +1,32 @@
# 附件自动关联后台任务实施计划
## 目标
把小财管家 AI 模式里的附件关联从前端会话内存任务改为后端可查询后台任务,保证用户退出或刷新当前会话后,附件关联仍能继续完成并可恢复状态。
## 执行清单
- [x] 定位当前断链根因:前端依赖 `File` 对象和内存 `Map`
- [x] 确认票据夹已有 `receipt_id`、源文件和关联状态能力。
- [x] 落开发方案文档。
- [x] 实现后端任务 schema 和内存任务池。
- [x] 实现后端任务 API。
- [x] 实现后端票据夹源文件归集到报销单明细。
- [x] 增加后端测试。
- [x] 实现前端任务创建、轮询和恢复。
- [x] 增加前端测试断言。
- [x] 执行容器后端定向测试。
- [x] 执行前端定向测试和构建。
## 验证结果
- 后端定向测试:`6 passed`
- 前端定向测试:`12 passed`
- 前端构建:通过,保留既有 chunk size warning。
- 运行时检查:新任务查询路由已加载,未知任务返回“附件关联任务不存在或已失效。”
## 关键决策
- 第一版使用后端内存任务池和 FastAPI `BackgroundTasks`,解决前端会话断链。
- 第一版不新增数据库任务表,服务重启后的任务恢复作为后续增强。
- 前端消息只保存 `job_id`、状态和票据引用,不再保存附件原件。

View File

@@ -1,410 +0,0 @@
# X-Financial Duplicate Code Refactor Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development or superpowers:executing-plans for multi-task execution. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Reduce duplicated business logic, renderer helpers, protocol constants, and test fixtures without changing existing behavior.
**Architecture:** Start with low-risk pure helpers, then move toward business contract consolidation. Each round must add or preserve regression tests before production code changes, and backend validation must run inside `x-financial-main`.
**Tech Stack:** Vue 3, Vite, Node test runner, FastAPI, SQLAlchemy, pytest, Docker Compose.
---
## Scope
This document records the duplicate-code audit from 2026-06-23 and defines a staged cleanup path. The first implementation slice is intentionally small: extract shared frontend conversation rendering helpers used by `markdown.js` and `aiConversationHtmlRenderer.js`.
The following areas are in scope:
- Frontend AI markdown / HTML trusted block normalization.
- Frontend reimbursement review panel model duplication.
- Workbench AI composer / attachment strip duplication.
- Backend application gate, fact extraction, amount/date/location parsing.
- Backend platform risk context helper duplication.
- Cross-layer status, expense type, document type, and risk-level taxonomy drift.
- Test helper duplication for DB sessions, FastAPI client overrides, and OCR fakes.
The following areas are out of scope for the first implementation slice:
- Changing application submission behavior.
- Changing reimbursement association decision flow.
- Changing API response shapes.
- Editing unrelated notification top bar changes already present in the worktree.
## Findings
### P0: Frontend Trusted HTML And Conversation Text Helpers
`web/src/utils/markdown.js` and `web/src/utils/aiConversationHtmlRenderer.js` both implement:
- `ALLOWED_COLON_HEADING_TITLES`
- `BUSINESS_FIELD_LABELS`
- `TRUSTED_HTML_ALLOWED_TAGS`
- `TRUSTED_HTML_ALLOWED_ATTRS`
- `splitColonHeadingLine`
- `normalizeBusinessFieldLine`
- `hasOnlyTrustedHtmlTags`
- `sanitizeTrustedHtmlBlock`
- `extractTrustedHtmlBlocks`
- `restoreTrustedHtmlBlocks`
Plan:
- [x] Create `web/src/utils/conversationTrustedHtml.js`.
- [x] Move trusted HTML sanitizing and business-line normalization into the helper.
- [x] Keep renderer-specific output differences in each renderer.
- [x] Verify both markdown and AI conversation renderers still preserve valid trusted document cards and reject unsafe trusted HTML.
Expected benefit:
- One XSS whitelist.
- One business field normalization rule.
- Less drift between AI workbench and reimbursement assistant rendering.
### P0: Reimbursement Review Panel Model Duplication
`web/src/views/scripts/travelReimbursementCreateReviewModel.js` and `web/src/views/scripts/travelReimbursementReviewPanelModel.js` duplicate review scope, fact cards, risk item mapping, risk conversation text, and message cleanup.
Plan:
- [x] Choose `travelReimbursementReviewPanelModel.js` as the shared model.
- [x] Convert create-view imports to the shared model, or make the create-view module a thin compatibility re-export.
- [x] Add behavior tests for exported review helpers before deleting duplicate code.
Expected benefit:
- One risk item mapping.
- One review fact card model.
- Lower risk when changing reimbursement review copy or drawer behavior.
### P0: Workbench AI Composer And File Strip Template Duplication
`web/src/components/business/PersonalWorkbenchAiMode.template.html` keeps two near-identical composer forms and two near-identical selected-file strips for the welcome and inline conversation states. `web/src/components/business/workbench-ai/WorkbenchAiComposer.vue` and `web/src/components/business/workbench-ai/WorkbenchAiFileStrip.vue` already exist, but the main template still duplicates the markup.
Plan:
- [x] Reuse `WorkbenchAiComposer.vue` for both welcome and inline composer surfaces.
- [x] Reuse `WorkbenchAiFileStrip.vue` for both welcome and inline selected-file strips.
- [x] Keep the parent runtime API stable by passing a proxied runtime object into shared components.
- [x] Preserve OCR state display in the shared file-strip component.
- [x] Keep input focus behavior by exposing an explicit assistant input ref setter.
Expected benefit:
- One composer markup surface.
- One selected-file/OCR badge markup surface.
- Lower maintenance cost when upload, date picker, lock state, or send-button behavior changes.
### P1: Application Gate And Fact Extraction
Backend application flow repeats checks across `user_agent_application.py`, `steward_planner.py`, `orchestrator.py`, `ontology_detection.py`, and `ontology_extraction.py`.
Plan:
- [ ] Extract application intent / context gate helpers.
- [ ] Extract application fact resolver for date, location, reason, amount, transport, and expense type.
- [ ] Route UserAgent, StewardPlanner, and Orchestrator through the same helpers.
- [ ] Preserve existing application vs reimbursement stage boundaries.
Expected benefit:
- Fewer mismatches between button actions and text-input actions.
- Less chance of direct submit re-entering slow `/orchestrator/run` paths.
- More consistent missing-field prompts.
### P1: Backend Parsing And Risk Context Utilities
Several backend modules repeat city extraction, document field lookup, item-id dedupe, Decimal conversion, endpoint normalization, and JSON error parsing.
Plan:
- [ ] Extract platform risk context helpers for item-id and document field utilities.
- [ ] Reuse existing amount utilities before adding new regex parsing.
- [ ] Share model connectivity URL/header/error helpers between RAG runtime and connectivity checks.
- [ ] Cache sorted travel-policy city names per policy snapshot.
Expected benefit:
- Less CPU churn in repeated risk/OCR loops.
- Fewer provider connectivity behavior differences.
- Easier review of platform-risk regressions.
### P1: Cross-Layer Taxonomy And Protocol Constants
Status labels, expense types, document types, risk levels, API paths, and snake_case/camelCase mappings are repeated across backend schemas, frontend services, and tests.
Plan:
- [ ] Establish read-only contract baseline from OpenAPI export.
- [ ] Export status / approval-stage registry to frontend constants.
- [ ] Consolidate expense type, document type, and risk-level taxonomy.
- [ ] Move high-churn API path and payload builders into shared frontend test helpers.
Expected benefit:
- Fewer display inconsistencies.
- Easier API evolution.
- Less brittle source-string tests.
### P2: Test Fixture Duplication
`server/tests` repeatedly defines `build_session`, `build_session_factory`, `override_db`, `build_client`, and OCR fake functions.
Plan:
- [ ] Add backend test fixtures in `server/tests/conftest.py`.
- [ ] Move OCR fake builders into a small test helper.
- [ ] Migrate tests in batches, one behavior area at a time.
Expected benefit:
- Less boilerplate in large test files.
- Easier targeted regression coverage before service refactors.
## First Slice Execution Plan
### Task 1: Lock Shared Renderer Helper Behavior
**Files:**
- Create: `web/tests/conversation-trusted-html.test.mjs`
- Create: `web/src/utils/conversationTrustedHtml.js`
- Modify: `web/src/utils/markdown.js`
- Modify: `web/src/utils/aiConversationHtmlRenderer.js`
Steps:
- [x] Add a failing Node test that imports `conversationTrustedHtml.js`.
- [x] Assert valid trusted document-card HTML is preserved through placeholder extraction and restore.
- [x] Assert unsafe tags, event handlers, and non-document hrefs are rejected.
- [x] Assert colon headings and business field lines normalize outside fenced code blocks.
- [x] Run the new test and confirm it fails because the helper does not exist.
- [x] Implement the helper with pure functions only.
- [x] Refactor both renderers to use the helper.
- [x] Run targeted renderer tests.
- [x] Run `npm --prefix web run build`.
Validation commands:
```bash
node --test web/tests/conversation-trusted-html.test.mjs
node --test web/tests/ai-conversation-html-renderer.test.mjs web/tests/travel-reimbursement-review-drawer-switch.test.mjs
npm --prefix web run build
```
## Third Slice Execution Plan
### Task 3: Reuse Workbench AI Composer Components
**Files:**
- Create: `web/tests/workbench-ai-composer-components.test.mjs`
- Modify: `web/src/components/business/PersonalWorkbenchAiMode.vue`
- Modify: `web/src/components/business/PersonalWorkbenchAiMode.template.html`
- Modify: `web/src/components/business/workbench-ai/WorkbenchAiFileStrip.vue`
- Modify: `web/src/composables/workbenchAiMode/usePersonalWorkbenchAiMode.js`
Steps:
- [x] Add a failing Node test that expects the main template to use `WorkbenchAiComposer` and `WorkbenchAiFileStrip`.
- [x] Assert the shared file strip preserves OCR state badges.
- [x] Assert the runtime exposes an input ref setter for the shared composer.
- [x] Run the new test and confirm it fails on the duplicated template.
- [x] Pass a proxied runtime object from `PersonalWorkbenchAiMode.vue` into shared components.
- [x] Replace duplicate composer and file-strip markup in the external template.
- [x] Add OCR badge markup to `WorkbenchAiFileStrip.vue`.
- [x] Run targeted workbench AI tests.
- [x] Run `npm --prefix web run build`.
Validation commands:
```bash
node --test web/tests/workbench-ai-composer-components.test.mjs
node --test web/tests/workbench-ai-composer-components.test.mjs web/tests/workbench-ai-mode-switch.test.mjs web/tests/workbench-ai-mode-expense-scene-action.test.mjs
npm --prefix web run build
```
## Remaining Execution Plan
The remaining work is intentionally split into bounded slices. Each slice extracts shared code without changing user-facing flow, API response shape, or submission semantics.
### Task 4: Frontend Application Gate Helpers
**Files:**
- Create: `web/src/composables/workbenchAiMode/workbenchAiApplicationGateModel.js`
- Create: `web/tests/workbench-ai-application-gate-model.test.mjs`
- Modify: `web/src/composables/workbenchAiMode/usePersonalWorkbenchAiMode.js`
- Modify: `web/src/composables/workbenchAiMode/useWorkbenchAiApplicationPreviewFlow.js`
Steps:
- [x] Add a failing Node test for reimbursement creation intent, submit/save text action resolution, and orphan preview detection.
- [x] Move pure gate predicates into `workbenchAiApplicationGateModel.js`.
- [x] Replace inline copies in personal workbench and application-preview flow.
- [x] Run targeted workbench AI application tests.
Validation commands:
```bash
node --test web/tests/workbench-ai-application-gate-model.test.mjs
node --test web/tests/workbench-ai-application-gate-model.test.mjs web/tests/workbench-ai-mode-switch.test.mjs web/tests/workbench-ai-action-router.test.mjs
```
### Task 5: Backend Application Fact Resolver
**Files:**
- Create: `server/src/app/services/application_fact_resolver.py`
- Create: `server/tests/test_application_fact_resolver.py`
- Modify: `server/src/app/services/steward_planner.py`
Steps:
- [x] Add failing pytest coverage for time, location, reason, transport, and task-type inference.
- [x] Extract pure resolver helpers that preserve existing planner output.
- [x] Route `StewardPlannerService` extraction wrappers through the resolver.
- [x] Run targeted planner/fact tests inside the active app container.
Validation commands:
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_application_fact_resolver.py server/tests/test_steward_planner.py
```
### Task 6: Backend Platform Risk Context Utilities
**Files:**
- Modify: `server/src/app/services/expense_claim_platform_context_tools.py`
- Create: `server/tests/test_expense_claim_platform_context_tools.py`
- Modify: `server/src/app/services/expense_claim_platform_route_risk.py`
- Modify: `server/src/app/services/expense_claim_platform_risk.py`
Steps:
- [x] Add failing pytest coverage for context city extraction, item-id dedupe, and text-value dedupe helpers.
- [x] Add pure helper functions in `expense_claim_platform_context_tools.py`.
- [x] Route route-risk and platform-risk consumers through the shared helpers.
- [x] Run targeted platform-risk tests inside the active app container.
Validation commands:
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_expense_claim_platform_context_tools.py server/tests/test_expense_claim_platform_risk_stage.py
```
### Task 7: Frontend Protocol Constants And Test Helpers
**Files:**
- Create: `web/src/constants/documentProtocol.js`
- Create: `web/tests/helpers/sourceSurface.mjs`
- Create: `web/tests/document-protocol-constants.test.mjs`
- Modify: `web/src/composables/workbenchAiMode/workbenchAiApplicationPreviewModel.js`
- Migrate one high-churn source-surface test to the helper.
Steps:
- [x] Add failing Node tests for status labels, document type constants, and reusable source-surface loading.
- [x] Move duplicated status labels into `documentProtocol.js`.
- [x] Reuse constants in application-preview model and request/document-center model code.
- [x] Migrate one source-surface test helper to reduce brittle boilerplate.
Validation commands:
```bash
node --test web/tests/document-protocol-constants.test.mjs web/tests/workbench-ai-mode-switch.test.mjs
```
### Task 8: Backend Test Fixture Consolidation
**Files:**
- Create: `server/src/app/test_helpers/db.py`
- Create: `server/src/app/test_helpers/__init__.py`
- Create: `server/tests/test_db_test_helpers.py`
- Modify: `server/tests/test_expense_claim_platform_risk_stage.py`
Steps:
- [x] Identify an existing small test file with duplicated session/client/OCR helpers.
- [x] Add shared DB helper while preserving its current behavior.
- [x] Migrate one test file only.
- [x] Run the migrated test plus adjacent coverage inside the active app container.
Validation commands:
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_db_test_helpers.py server/tests/test_expense_claim_platform_risk_stage.py
```
### Task 9: Steward Planner Module Split
**Files:**
- Create: `server/src/app/services/steward_planner_shared.py`
- Create: `server/src/app/services/steward_planner_fallback.py`
- Create: `server/src/app/services/steward_planner_extraction.py`
- Modify: `server/src/app/services/steward_planner.py`
Steps:
- [x] Move shared constants and `PlannedTaskDraft` into a shared planner module.
- [x] Move off-topic, pending-flow, and rule-fallback planning into `steward_planner_fallback.py`.
- [x] Move task extraction, ontology normalization, attachment grouping, and summary helpers into `steward_planner_extraction.py`.
- [x] Keep `steward_planner.py` as the service orchestration entrypoint.
- [x] Run planner/fact resolver tests inside the active app container.
Validation commands:
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q server/tests/test_application_fact_resolver.py server/tests/test_steward_planner.py
```
### Task 10: App Shell Dynamic Business Chunk Loading
**Files:**
- Create: `web/src/views/scripts/appShellAsyncViews.js`
- Create: `web/src/components/shared/AppViewLoadingState.vue`
- Create: `web/src/components/shared/AppModalLoadingState.vue`
- Modify: `web/src/views/AppShellRouteView.vue`
- Modify: `web/src/components/layout/SidebarRail.vue`
- Modify: `web/src/components/layout/AiSidebarRail.vue`
- Modify: `web/tests/app-shell-route-loading.test.mjs`
- Modify: `web/tests/ai-sidebar-rail-mode.test.mjs`
Steps:
- [x] Keep top-level shell routes eager so login/setup/app layout does not blank during route navigation.
- [x] Move heavy business views behind `defineAsyncComponent` loaders.
- [x] Add an in-workarea loading state for slow business chunks.
- [x] Add a modal loading state for the smart reimbursement assistant chunk.
- [x] Preload likely next views on sidebar hover/focus and during browser idle time.
- [x] Preserve existing Vite manual vendor chunks, including `vendor-echarts`.
- [x] Run targeted frontend tests and production build.
Validation commands:
```bash
node --test web/tests/app-shell-route-loading.test.mjs web/tests/ai-sidebar-rail-mode.test.mjs web/tests/sidebar-document-unread-dot.test.mjs web/tests/workbench-ai-mode-switch.test.mjs web/tests/workbench-ai-reimbursement-association-gate.test.mjs web/tests/travel-reimbursement-review-drawer-switch.test.mjs web/tests/documents-center-status-filter.test.mjs
npm --prefix web run build
```
Result:
- App shell entry chunk after split: `index-vWyUfHfm.js` 223.75 kB, gzip 74.11 kB.
- Large `vendor-echarts` chunk remains isolated at 598.67 kB, gzip 204.84 kB; it is no longer part of the app-shell entry chunk.
- Build still reports the existing Rollup `#__PURE__` annotation warnings from Element Plus / VueUse.
## Guardrails
- Do not touch unrelated dirty files.
- Do not change renderer output intentionally in this slice.
- Do not move backend logic until frontend helper extraction is green.
- Backend tests must run through Docker:
```bash
docker exec -w /app -e SERVER_VENV_DIR=/tmp/x-financial-server-venv x-financial-main /tmp/x-financial-server-venv/bin/pytest -q <path>
```