refactor: establish standalone application boundary
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
# ADR 0001: Govern as a Modular Monolith
|
||||
|
||||
Status: Accepted
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Context
|
||||
|
||||
The application is deployed on a LAN NAS as one Docker container with a Python HTTP process,
|
||||
SQLite, a build-free browser client, scheduled refresh work, external market providers, and
|
||||
LLM features. Product breadth has grown, while routing, persistence, frontend state, and CSS
|
||||
remain concentrated in a few large files.
|
||||
|
||||
The system may later become internet-facing, gain more features, replace SQLite, or move jobs
|
||||
to workers. It does not currently have load or team boundaries that justify distributed
|
||||
services.
|
||||
|
||||
## Decision
|
||||
|
||||
Retain one deployable application and introduce strict internal modules, ports, adapters,
|
||||
feature registries, data contracts, repository contracts, and regression gates.
|
||||
|
||||
The deployment remains:
|
||||
|
||||
```text
|
||||
one image + one application process + one persistent data volume + port 8765
|
||||
```
|
||||
|
||||
Internal code moves toward:
|
||||
|
||||
```text
|
||||
delivery -> application services -> ports -> infrastructure
|
||||
```
|
||||
|
||||
Compatibility facades permit incremental migration. No feature is rewritten solely to match
|
||||
the target directory structure.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Current NAS deployment stays simple.
|
||||
- Refactoring can proceed in reversible stages.
|
||||
- Feature ownership and account boundaries become visible.
|
||||
- Data, database, LLM, and job adapters can be replaced later.
|
||||
- A measured hotspot can be extracted without first untangling business logic.
|
||||
|
||||
### Costs
|
||||
|
||||
- The transition temporarily contains old and new entrypoints.
|
||||
- Boundary tests and registries require ongoing maintenance.
|
||||
- A single process remains a capacity and fault-isolation limit until infrastructure is
|
||||
deliberately extracted.
|
||||
|
||||
## Rejected Alternatives
|
||||
|
||||
### Immediate microservices
|
||||
|
||||
Rejected because they add network contracts, service discovery, deployment coordination,
|
||||
distributed tracing, and failure modes before load requires them.
|
||||
|
||||
### Full framework rewrite
|
||||
|
||||
Rejected because replacing the HTTP and frontend frameworks while moving boundaries would
|
||||
combine structural and behavioral risk.
|
||||
|
||||
### Continue patching flat modules
|
||||
|
||||
Rejected because current file size, direct provider creation, global page state, and CSS
|
||||
override layers already make regressions difficult to isolate.
|
||||
|
||||
## Revisit Conditions
|
||||
|
||||
Reconsider service extraction when one module has independently measured scaling needs,
|
||||
requires a separate availability boundary, or needs an independent release lifecycle.
|
||||
@@ -0,0 +1,317 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"captured_from": "governed source tree",
|
||||
"runtime": {
|
||||
"http_server": "http.server.ThreadingHTTPServer",
|
||||
"application_processes": 1,
|
||||
"database": "SQLite WAL",
|
||||
"frontend": "build-free HTML/CSS/JavaScript",
|
||||
"container_port": 8765
|
||||
},
|
||||
"counts": {
|
||||
"primary_pages": 16,
|
||||
"api_exact_paths": 53,
|
||||
"api_prefixes": 0,
|
||||
"api_patterns": 11,
|
||||
"database_tables": 34
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"id": "sentimentCycleView",
|
||||
"title": "情绪周期"
|
||||
},
|
||||
{
|
||||
"id": "limitPool",
|
||||
"title": "涨停池"
|
||||
},
|
||||
{
|
||||
"id": "brokenView",
|
||||
"title": "炸板池"
|
||||
},
|
||||
{
|
||||
"id": "downView",
|
||||
"title": "跌停板"
|
||||
},
|
||||
{
|
||||
"id": "yesterdayView",
|
||||
"title": "昨日涨停"
|
||||
},
|
||||
{
|
||||
"id": "performanceView",
|
||||
"title": "涨停表现"
|
||||
},
|
||||
{
|
||||
"id": "ladderView",
|
||||
"title": "市场天梯"
|
||||
},
|
||||
{
|
||||
"id": "rotationView",
|
||||
"title": "板块轮动"
|
||||
},
|
||||
{
|
||||
"id": "auctionView",
|
||||
"title": "集合竞价"
|
||||
},
|
||||
{
|
||||
"id": "themeLibraryView",
|
||||
"title": "题材库"
|
||||
},
|
||||
{
|
||||
"id": "popularityView",
|
||||
"title": "人气热榜"
|
||||
},
|
||||
{
|
||||
"id": "dragonView",
|
||||
"title": "龙虎榜"
|
||||
},
|
||||
{
|
||||
"id": "screenerView",
|
||||
"title": "智能选股"
|
||||
},
|
||||
{
|
||||
"id": "mentorView",
|
||||
"title": "问师"
|
||||
},
|
||||
{
|
||||
"id": "heavenView",
|
||||
"title": "问天"
|
||||
},
|
||||
{
|
||||
"id": "reviewWorkspaceView",
|
||||
"title": "我的复盘"
|
||||
}
|
||||
],
|
||||
"api": {
|
||||
"exact": [
|
||||
"/api/account/birth-profile",
|
||||
"/api/account/password",
|
||||
"/api/account/status",
|
||||
"/api/admin/membership",
|
||||
"/api/admin/refresh",
|
||||
"/api/admin/settings",
|
||||
"/api/admin/settings/test",
|
||||
"/api/alerts",
|
||||
"/api/alerts/read-all",
|
||||
"/api/assistant/chat",
|
||||
"/api/assistant/messages",
|
||||
"/api/auction",
|
||||
"/api/auth/login",
|
||||
"/api/auth/logout",
|
||||
"/api/auth/me",
|
||||
"/api/auth/register",
|
||||
"/api/backfill",
|
||||
"/api/chart/intraday",
|
||||
"/api/dashboard",
|
||||
"/api/dragon-tiger",
|
||||
"/api/dragon-tiger/profiles",
|
||||
"/api/health",
|
||||
"/api/heaven/hexagram",
|
||||
"/api/heaven/interpret",
|
||||
"/api/heaven/personal",
|
||||
"/api/heaven/readings",
|
||||
"/api/heaven/sector-phases",
|
||||
"/api/heaven/setup",
|
||||
"/api/mentors/chat",
|
||||
"/api/mentors/messages",
|
||||
"/api/mentors/preferences",
|
||||
"/api/mentors/setup",
|
||||
"/api/notes",
|
||||
"/api/popularity",
|
||||
"/api/realtime-aggregate/health",
|
||||
"/api/reasons",
|
||||
"/api/rotation/history",
|
||||
"/api/rotation/members",
|
||||
"/api/screener/compile",
|
||||
"/api/screener/run",
|
||||
"/api/screener/setup",
|
||||
"/api/screener/strategies",
|
||||
"/api/screener/sync",
|
||||
"/api/screener/tracking",
|
||||
"/api/screener/tracking/refresh",
|
||||
"/api/search",
|
||||
"/api/search/detail",
|
||||
"/api/seat-aliases",
|
||||
"/api/sentiment/history",
|
||||
"/api/themes",
|
||||
"/api/themes/detail",
|
||||
"/api/trades",
|
||||
"/api/watchlist"
|
||||
],
|
||||
"prefixes": [],
|
||||
"patterns": [
|
||||
"/api/alerts/(\\d+)",
|
||||
"/api/alerts/(\\d+)/read",
|
||||
"/api/heaven/readings/(\\d+)",
|
||||
"/api/heaven/sector-phases/(.+)",
|
||||
"/api/notes/(\\d+)",
|
||||
"/api/screener/strategies/(\\d+)",
|
||||
"/api/screener/tracking/(\\d+)",
|
||||
"/api/stock/(\\d{6})",
|
||||
"/api/stock/(\\d{6})/preview",
|
||||
"/api/trades/(\\d+)",
|
||||
"/api/watchlist/(\\d{6})"
|
||||
]
|
||||
},
|
||||
"database_tables": [
|
||||
"users",
|
||||
"user_sessions",
|
||||
"user_credentials",
|
||||
"user_birth_profiles",
|
||||
"system_settings",
|
||||
"llm_usage",
|
||||
"dashboard_snapshots",
|
||||
"sync_runs",
|
||||
"data_snapshots",
|
||||
"watchlist",
|
||||
"review_notes",
|
||||
"reason_overrides",
|
||||
"seat_aliases",
|
||||
"sector_phase_overrides",
|
||||
"stock_master",
|
||||
"daily_bars",
|
||||
"benchmark_bars",
|
||||
"daily_indicators",
|
||||
"fundamental_indicators",
|
||||
"moneyflow_daily",
|
||||
"auction_factors",
|
||||
"earnings_events",
|
||||
"popularity_factors",
|
||||
"lhb_institution_daily",
|
||||
"screener_strategies",
|
||||
"screener_runs",
|
||||
"mentor_messages",
|
||||
"mentor_preferences",
|
||||
"wencai_saved_queries",
|
||||
"strategy_tracks",
|
||||
"alerts",
|
||||
"trade_entries",
|
||||
"assistant_messages",
|
||||
"heaven_readings"
|
||||
],
|
||||
"background_job_methods": [
|
||||
"_background_refresh_tick",
|
||||
"_schedule_automatic_screeners",
|
||||
"_schedule_ifind_event_enrichment",
|
||||
"run_automatic_screeners"
|
||||
],
|
||||
"external_data_adapters": [
|
||||
{
|
||||
"provider": "tushare",
|
||||
"path": "tushare_client.py",
|
||||
"runtime_role": "primary deterministic market data"
|
||||
},
|
||||
{
|
||||
"provider": "ifind",
|
||||
"path": "ifind_client.py",
|
||||
"runtime_role": "realtime, charts, snapshots, enrichment"
|
||||
},
|
||||
{
|
||||
"provider": "eastmoney",
|
||||
"path": "chart_data_provider.py",
|
||||
"runtime_role": "display chart fallback"
|
||||
},
|
||||
{
|
||||
"provider": "eastmoney",
|
||||
"path": "realtime_aggregator.py",
|
||||
"runtime_role": "isolated realtime observation"
|
||||
},
|
||||
{
|
||||
"provider": "tencent",
|
||||
"path": "realtime_aggregator.py",
|
||||
"runtime_role": "index observation fallback"
|
||||
}
|
||||
],
|
||||
"llm_entrypoints": [
|
||||
{
|
||||
"function": "stream_with_mentor",
|
||||
"path": "mentor_agent.py"
|
||||
},
|
||||
{
|
||||
"function": "interpret_heaven",
|
||||
"path": "heaven_agent.py"
|
||||
},
|
||||
{
|
||||
"function": "stream_review_assistant",
|
||||
"path": "assistant_agent.py"
|
||||
},
|
||||
{
|
||||
"function": "compile_strategy_with_llm",
|
||||
"path": "llm_strategy.py"
|
||||
},
|
||||
{
|
||||
"function": "test_llm_connection",
|
||||
"path": "llm_strategy.py"
|
||||
}
|
||||
],
|
||||
"css_layers": [
|
||||
"/shared/tokens.css?v=20260729-1",
|
||||
"/styles.css",
|
||||
"/renovation.css?v=20260725-5",
|
||||
"/redesign-v2.css?v=20260728-1",
|
||||
"/design-system.css?v=20260728-4",
|
||||
"/theme.css?v=20260728-2",
|
||||
"/wentian-v2.css?v=20260728-7"
|
||||
],
|
||||
"code_hotspots": [
|
||||
{
|
||||
"path": "static/app.js",
|
||||
"bytes": 441313,
|
||||
"lines": 9283
|
||||
},
|
||||
{
|
||||
"path": "static/styles.css",
|
||||
"bytes": 361780,
|
||||
"lines": 15465
|
||||
},
|
||||
{
|
||||
"path": "server.py",
|
||||
"bytes": 263744,
|
||||
"lines": 5856
|
||||
},
|
||||
{
|
||||
"path": "static/redesign-v2.css",
|
||||
"bytes": 263539,
|
||||
"lines": 8570
|
||||
},
|
||||
{
|
||||
"path": "static/index.html",
|
||||
"bytes": 134831,
|
||||
"lines": 1890
|
||||
},
|
||||
{
|
||||
"path": "database.py",
|
||||
"bytes": 121546,
|
||||
"lines": 2839
|
||||
},
|
||||
{
|
||||
"path": "screener.py",
|
||||
"bytes": 108535,
|
||||
"lines": 2213
|
||||
},
|
||||
{
|
||||
"path": "tushare_client.py",
|
||||
"bytes": 94312,
|
||||
"lines": 2175
|
||||
},
|
||||
{
|
||||
"path": "static/renovation.css",
|
||||
"bytes": 83949,
|
||||
"lines": 1553
|
||||
},
|
||||
{
|
||||
"path": "static/wentian-v2.css",
|
||||
"bytes": 73222,
|
||||
"lines": 1084
|
||||
},
|
||||
{
|
||||
"path": "market_insights.py",
|
||||
"bytes": 58066,
|
||||
"lines": 1312
|
||||
},
|
||||
{
|
||||
"path": "static/theme.css",
|
||||
"bytes": 36427,
|
||||
"lines": 1253
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
# Application Architecture Standard
|
||||
|
||||
Status: Accepted
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## 1. Objective
|
||||
|
||||
The application remains a deployable modular monolith while its internal boundaries are made
|
||||
explicit. Governance must preserve current functionality, account isolation, visual behavior,
|
||||
and Docker simplicity. A structural refactor is not permission to change product behavior.
|
||||
|
||||
## 2. Target Topology
|
||||
|
||||
```text
|
||||
browser
|
||||
-> frontend shared API client
|
||||
-> backend HTTP controllers
|
||||
-> feature application services
|
||||
-> repositories / DataGateway / LLMGateway
|
||||
-> SQLite / market providers / model providers
|
||||
|
||||
background jobs
|
||||
-> the same feature application services
|
||||
-> the same repositories and gateways
|
||||
```
|
||||
|
||||
The browser and jobs are two delivery mechanisms. Neither owns business rules.
|
||||
|
||||
## 3. Target Source Layout
|
||||
|
||||
```text
|
||||
frontend/
|
||||
shared/
|
||||
tokens.css
|
||||
shell.js
|
||||
api.js
|
||||
state.js
|
||||
components/
|
||||
pages/<feature>/
|
||||
page.js
|
||||
desktop.css
|
||||
mobile.css
|
||||
pages.config.js
|
||||
|
||||
backend/
|
||||
bootstrap/
|
||||
http/
|
||||
router.py
|
||||
auth.py
|
||||
errors.py
|
||||
schemas/
|
||||
features/<feature>/
|
||||
routes.py
|
||||
service.py
|
||||
repository.py
|
||||
schemas.py
|
||||
data/
|
||||
gateway.py
|
||||
policy.py
|
||||
contracts.py
|
||||
providers/
|
||||
database/
|
||||
connection.py
|
||||
migrations/
|
||||
repositories/
|
||||
jobs/
|
||||
llm/
|
||||
gateway.py
|
||||
usage.py
|
||||
models.py
|
||||
prompts/
|
||||
features_config.py
|
||||
|
||||
tests/
|
||||
data/
|
||||
server.py
|
||||
```
|
||||
|
||||
The migration may use compatibility facades. Old modules are removed only after all callers
|
||||
move and regression gates pass.
|
||||
|
||||
## 4. Dependency Direction
|
||||
|
||||
Allowed direction:
|
||||
|
||||
```text
|
||||
HTTP / jobs -> feature services -> ports -> infrastructure adapters
|
||||
frontend pages -> shared components/state/API -> backend API
|
||||
```
|
||||
|
||||
Forbidden dependencies:
|
||||
|
||||
1. Frontend code must not call external market or LLM providers directly.
|
||||
2. HTTP controllers must not contain scoring, screening, divination, or persistence logic.
|
||||
3. Feature services must not instantiate Tushare, iFinD, Eastmoney, Tencent, SQLite, or LLM
|
||||
clients directly.
|
||||
4. Provider adapters must not import feature services.
|
||||
5. Repositories must not call external providers.
|
||||
6. Features must not read another feature's tables directly; cross-feature work goes through
|
||||
an application service or declared read model.
|
||||
7. Background jobs must call the same services used by HTTP flows rather than duplicate
|
||||
calculations.
|
||||
|
||||
## 5. Feature Ownership
|
||||
|
||||
Each feature owns its routes, application service, schemas, repository interface, page module,
|
||||
tests, and documentation. Initial feature IDs are defined in `features.config.json` during
|
||||
Stage 04.
|
||||
|
||||
Shared code is allowed only when at least two features use the same stable behavior. A shared
|
||||
module must not branch on page names or feature IDs to emulate unrelated components.
|
||||
|
||||
## 6. HTTP Contract
|
||||
|
||||
- Public application APIs use `/api/<resource>` and UTF-8 JSON.
|
||||
- Controllers validate transport input, authorize the request, call one application service,
|
||||
and serialize the result.
|
||||
- Dates exposed to users use `YYYY-MM-DD`; provider-specific compact dates stay inside
|
||||
adapters.
|
||||
- Datetimes include an explicit timezone. Market time is interpreted as Asia/Shanghai.
|
||||
- Successful collections use `items`; pagination uses `page`, `page_size`, and `total` when
|
||||
required.
|
||||
- Errors use a stable `code`, a user-safe `message`, and a request correlation ID. Provider
|
||||
credentials and raw response bodies never appear in browser errors.
|
||||
- Existing response shapes remain compatible until a versioned migration is approved.
|
||||
- Backend authorization is authoritative. Hidden frontend controls are not a security rule.
|
||||
|
||||
## 7. Data Contract
|
||||
|
||||
Every calculation field declares:
|
||||
|
||||
- canonical field ID and Chinese display label;
|
||||
- entity and frequency;
|
||||
- type, unit, precision, timezone, and adjustment mode;
|
||||
- unique primary source and permitted fallback sources;
|
||||
- display-only or calculation-eligible status;
|
||||
- freshness and completeness thresholds;
|
||||
- missing-value behavior;
|
||||
- point-in-time availability rules;
|
||||
- owning dataset and persistence location.
|
||||
|
||||
Missing calculation data fails closed. A strategy may not silently replace a required field
|
||||
with a proxy and continue under the original strategy name. Display fallbacks cannot enter
|
||||
screening, scoring, backtesting, sentiment, or Wentian calculations unless explicitly approved
|
||||
for that canonical field.
|
||||
|
||||
One OHLC bar or factor history must not mix providers. Source changes are recorded with the
|
||||
stored observation and invalidate incompatible cached calculations.
|
||||
|
||||
## 8. Market Provider Policy
|
||||
|
||||
- Tushare is the default deterministic source for master data, calendar, daily bars,
|
||||
fundamentals, valuations, industry data, lists, and post-close datasets.
|
||||
- iFinD is the default source for licensed realtime snapshots, intraday data, dynamic auction
|
||||
observations, charts, and approved event enrichment.
|
||||
- Eastmoney and Tencent public endpoints are display or observation fallbacks only unless a
|
||||
field contract explicitly promotes them.
|
||||
- All provider traffic passes through `DataGateway`; provider classes only translate their own
|
||||
protocol into canonical contracts.
|
||||
- Retries, timeouts, quotas, cache TTL, circuit state, and provenance are centralized.
|
||||
|
||||
## 9. Persistence Standard
|
||||
|
||||
- SQLite WAL remains the current deployment database.
|
||||
- Schema changes use ordered migration files with an immutable version ID.
|
||||
- A migration is transactional where SQLite permits it and must be safe to run once.
|
||||
- Destructive migrations require a verified backup and explicit acceptance.
|
||||
- User-owned tables include `user_id`, a foreign key, an ownership index, and cross-account
|
||||
tests.
|
||||
- Repositories return domain-shaped records; controllers never execute SQL.
|
||||
- Financial and research data preserve announcement timestamps to prevent look-ahead use.
|
||||
- A future PostgreSQL adapter must satisfy the same repository contracts.
|
||||
|
||||
## 10. Background Jobs
|
||||
|
||||
- Jobs declare an ID, schedule, input date policy, dependencies, lock key, timeout, retry
|
||||
policy, and idempotency key.
|
||||
- Job runs persist start, completion, failure code, retry count, source coverage, and output
|
||||
version.
|
||||
- A process restart must not duplicate a completed post-close calculation.
|
||||
- Exceptions are logged and surfaced in system management; they are never silently discarded.
|
||||
- The current in-process runner may remain, but jobs cannot depend on thread-local request
|
||||
context. This permits later extraction to a worker without changing business services.
|
||||
|
||||
## 11. LLM Standard
|
||||
|
||||
All model calls pass through `LLMGateway`, which owns:
|
||||
|
||||
- membership authorization and feature availability;
|
||||
- quota reservation, settlement, and daily limits;
|
||||
- model-pool selection and fallback;
|
||||
- timeout, cancellation, retry, and streaming protocol;
|
||||
- prompt version, model, latency, token usage, and failure audit;
|
||||
- removal of secrets and engineering details from user-visible errors.
|
||||
|
||||
Skill evidence, source material, and data profiles remain separate from reusable prompt
|
||||
templates. Deterministic calculations happen before the LLM call and are not delegated to the
|
||||
model.
|
||||
|
||||
## 12. Frontend Standard
|
||||
|
||||
- `shared/api.js` is the only browser request exit.
|
||||
- `shared/shell.js` owns sidebar, topbar, market summary, status bar, global dialogs, and page
|
||||
mounting.
|
||||
- `pages.config.js` owns navigation metadata and page loading, not authorization truth.
|
||||
- A page module fetches data, owns page-local state, and composes shared components.
|
||||
- Global mutable page state and cross-page DOM queries are prohibited after migration.
|
||||
- Tables use shared shells with page-owned column schemas and formatters.
|
||||
- Opening, closing, focus management, and feedback for dialogs use one dialog service.
|
||||
|
||||
## 13. CSS and Design Tokens
|
||||
|
||||
The cascade order is fixed:
|
||||
|
||||
```text
|
||||
tokens -> reset/base -> shell/layout -> shared components -> page styles -> theme overrides
|
||||
```
|
||||
|
||||
- Literal colors, font sizes, and spacing are introduced through tokens first.
|
||||
- Page styles are scoped to the page root and cannot redefine the shell or another page.
|
||||
- A later file cannot be used indefinitely as a patch layer for an earlier file.
|
||||
- Shared components have one authoritative definition.
|
||||
- CSS is removed only after selector/reference scanning, light/dark screenshots, and full
|
||||
browser regression.
|
||||
|
||||
## 14. Mobile Standard
|
||||
|
||||
Mobile shares tokens, data, permissions, components, and state with desktop, but it may use a
|
||||
different composition and interaction model.
|
||||
|
||||
- Shared mobile shell rules live in `shared/mobile-shell.css`.
|
||||
- Each complex page may own `mobile.css` and a mobile view renderer.
|
||||
- One global all-page `mobile.css` override pile is prohibited.
|
||||
- Desktop tables may become summary lists and detail views on mobile.
|
||||
- Mobile acceptance is performed independently at 390x844 and 430x932.
|
||||
- Mobile changes must not alter approved desktop geometry.
|
||||
|
||||
## 15. Configuration and Secrets
|
||||
|
||||
- Environment variables seed deployment configuration; encrypted system settings hold managed
|
||||
runtime credentials.
|
||||
- Tokens, API keys, refresh tokens, model secrets, and encryption keys never enter Git, logs,
|
||||
API responses, or browser storage.
|
||||
- Feature flags, page metadata, field contracts, and permissions are versioned configuration,
|
||||
not ad hoc conditionals.
|
||||
|
||||
## 16. Observability
|
||||
|
||||
External calls record provider, operation, elapsed time, cache result, freshness, normalized
|
||||
error code, and correlation ID. Logs must not contain secrets or personal birth data.
|
||||
|
||||
Health checks distinguish process health, database health, provider degradation, background
|
||||
job health, and model availability. Provider degradation does not make the process health
|
||||
endpoint fail unless the application itself cannot serve stored data.
|
||||
|
||||
## 17. Change and Release Contract
|
||||
|
||||
Every governance phase must:
|
||||
|
||||
1. begin from a clean Git worktree;
|
||||
2. preserve the Stage 01 baseline contract;
|
||||
3. include migration or compatibility tests for changed boundaries;
|
||||
4. pass `python tools/verify_baseline.py`;
|
||||
5. run Playwright at phase boundaries affecting runtime or frontend behavior;
|
||||
6. document residual risk;
|
||||
7. create and push a dedicated rollback commit.
|
||||
|
||||
No phase combines framework replacement, visual redesign, and business-rule changes.
|
||||
|
||||
## 18. Public-Internet Evolution
|
||||
|
||||
The modular monolith remains the default. Reverse proxy/TLS, PostgreSQL, Redis, durable jobs,
|
||||
central secrets, monitoring, and rate limiting can replace infrastructure adapters later.
|
||||
Feature services and frontend contracts must not depend on whether those adapters are local or
|
||||
remote. A feature is extracted into a separate service only when measured load, independent
|
||||
release needs, or fault isolation justifies the operational cost.
|
||||
@@ -0,0 +1,401 @@
|
||||
# `app/`代码减法账本
|
||||
|
||||
> 基线:`xiaobai-preservation-complete-20260801`
|
||||
> 工作目录:只允许修改`webapp/app/`;原版根目录和冻结的`next/`只读
|
||||
> 目标:删除重复实现和历史补丁,不改变功能、视觉、交互、动画、计算、权限、API或数据行为
|
||||
|
||||
## 固定规则
|
||||
|
||||
1. 每批只处理一个明确边界,先证明重复或无消费者,再修改。
|
||||
2. 新共享实现必须在同一提交删除全部被替代实现;禁止只加一层包装。
|
||||
3. 运行代码总量原则上不得增加;测试和证据代码单独统计。
|
||||
4. 迁移期源码相等测试不得简单删除。发生已批准的结构重构时,必须替换成行为、错误语义和
|
||||
唯一所有权契约。
|
||||
5. 每批通过领域测试、全量候选测试、独立导出测试和受影响的浏览器流程后才建立Git检查点。
|
||||
6. CSS最后处理;没有逐页日间、夜间和多视口截图证据,不删除视觉规则。
|
||||
|
||||
## 批次记录
|
||||
|
||||
| 批次 | 边界 | 基线问题 | 目标 | 状态 |
|
||||
|---|---|---|---|---|
|
||||
| CR-01 | LLM供应商传输 | 问师、问天、复盘助手和策略编译各自构造HTTP请求、解析响应和读取错误 | 只保留`backend/llm/transport.py`一个网络出口 | 已完成 |
|
||||
| CR-02 | HTTP精确POST委托 | 27个端点重复使用“比较路径、调用无参数处理器、返回”三行分支 | 用公开/受保护两张显式映射统一委托,同时保留复杂路由的原控制流 | 已完成 |
|
||||
| CR-03 | 股票市场后缀转换 | Tushare业务与iFinD图表各保留一份完全相同的沪深京代码转换函数 | 图表复用`bootstrap/config.py::tushare_code`,只保留一份函数体 | 已完成 |
|
||||
| CR-04 | 数值归一化策略 | 四个业务模块分别保留两组完全相同的数值转换函数体 | 由`backend/data/numbers.py`集中拥有两种既有语义,消费者保留原局部别名 | 已完成 |
|
||||
| CR-05 | 根级兼容入口 | 正式后端仍有五处通过迁移兼容模块反向导入规范实现 | 正式代码改用规范路径;兼容入口只服务原公开导入契约 | 已完成 |
|
||||
| CR-06 | 紧凑日期显示 | Tushare与选股引擎各保留一份完全相同的`YYYYMMDD`显示转换 | 由`bootstrap/config.py`拥有唯一格式策略,消费者保留原局部别名 | 已完成 |
|
||||
| CR-07 | 数据Provider组装 | 核查网关、容器和业务服务是否重复创建外部数据客户端 | 固化唯一创建位置及兼容例外,不改数据源语义 | 已完成 |
|
||||
| CR-08 | NDJSON流式传输 | 问师与复盘助手重复维护响应头、事件写入、完成、断线及关闭流程 | HTTP共享层拥有唯一流式连接生命周期 | 已完成 |
|
||||
| CR-09 | 应用服务门面 | 两个仅服务股池iFinD补全的方法仍错位在全局`DashboardService` | 原函数体机械归位到`PoolServiceMixin` | 已完成 |
|
||||
| CR-10 | Repository所有权 | 五行行业阶段覆盖的三项持久化方法仍错位在根级数据库门面 | 原函数体机械归位到问天Repository,兼容数据继续保留 | 已完成 |
|
||||
| CR-11 | 后台任务生命周期 | 导入应用即启动调度线程,启动早于端口绑定,停止只置位但不等待 | 运行时显式启停,每个Runner只拥有一个可等待的调度线程 | 已完成 |
|
||||
| CR-12 | CSS跨层精确重复 | 七层样式保留多次视觉改造形成的重复顶层规则,前层声明被后层逐字覆盖 | 只删除能够由CSSOM证明完全重复的前层规则,并建立浏览器级重复门禁 | 已完成 |
|
||||
| CR-13 | CSS跨文件嵌套精确重复 | 相同媒体上下文中的完整规则分散在不同样式文件,前层声明仍被后层完整重复 | 递归核对CSSOM上下文,只退休跨文件的精确副本 | 已完成 |
|
||||
| CR-14 | CSS同文件精确重复 | 同一文件、同一媒体上下文仍保留多轮视觉调整形成的完整重复规则 | 删除较早副本并把同文件重复纳入浏览器门禁 | 已完成 |
|
||||
|
||||
## CR-01验收口径
|
||||
|
||||
- 四个功能模块不得包含`urllib.request`或`/chat/completions`。
|
||||
- 非流式与流式请求的URL、鉴权、User-Agent、SSE累积和空响应行为保持不变。
|
||||
- 各功能原有错误类型和用户可见错误文案保持不变。
|
||||
- `LLMGateway`的会员、额度、主辅回退、首字后不中途换模型和审计规则保持不变。
|
||||
- 架构清单必须登记唯一传输入口;完整测试和真实主模型最小调用通过。
|
||||
|
||||
## CR-01结果
|
||||
|
||||
- 四个功能模块的运行代码由572行降至422行;新增唯一传输实现129行,生产代码净减少21行、
|
||||
约1.4 KB。行数不是主要收益,关键是5处`/chat/completions`请求只剩1处。
|
||||
- 三套重复HTTP错误正文解析合并为一套;各功能原有错误类型和用户可见文案由专项测试固定。
|
||||
- 迁移期4个“Agent文件逐字相等”断言没有直接删除,而是替换为共享传输唯一所有权、提示词模块
|
||||
归属、SSE行为和兼容模块对象契约。
|
||||
- 候选311项、纯`app/`导出248项、45项Playwright通过;24个JavaScript文件、架构/API注册表和
|
||||
SQLite完整性检查通过。
|
||||
- 使用候选数据库中加密保存的主模型完成真实非流式与流式最小调用,分别成功返回完整响应和
|
||||
4个流式分片。调用未输出密钥或模型正文。
|
||||
- 本批不修改页面、CSS、提示词、业务计算、会员额度、模型回退、数据库或部署。
|
||||
|
||||
回档基线为`xiaobai-preservation-complete-20260801`;本批检查点为
|
||||
`xiaobai-reduction-01-llm-transport-20260801`。
|
||||
|
||||
## CR-02验收口径
|
||||
|
||||
- 仅纳入没有路径参数、请求体解析或专属异常分支的精确POST端点;其余路由保持原样。
|
||||
- 公开注册/登录端点继续在鉴权前分发;受保护端点继续严格执行登录、CSRF、注册表权限、处理器。
|
||||
- 27个映射路径必须都由权威API注册表解析,处理方法必须真实存在,公开与受保护集合不得重叠。
|
||||
- API路径、功能归属、访问角色、状态码、错误正文和静态页面绕过鉴权行为保持不变。
|
||||
- API清单生成器必须结构化读取显式映射;架构清查复用API清单,不再维护第二套路由发现规则。
|
||||
|
||||
## CR-02结果
|
||||
|
||||
- 2个公开端点和25个受保护端点改为显式委托映射,原来的79行重复分支被43行映射、分发与调用替代;
|
||||
`backend/application.py`净减少36行,规范化源码约减少1.0 KB。
|
||||
- 带正则路径参数、请求体读取、查询参数转换或特殊异常语义的GET、POST、DELETE端点未改动。
|
||||
- 架构清查删除了自行扫描精确/正则API路径的第二套规则,改为消费权威`api.config.json`;API注册表的
|
||||
53个精确路径、11个正则路径、功能归属和权限均未变化。
|
||||
- 原版231项、候选315项、纯`app/`导出252项、45项Playwright通过;24个JavaScript文件、
|
||||
API/架构注册表、Git空白检查和SQLite完整性检查通过。
|
||||
- 本批不修改前端、CSS、业务计算、数据源、数据库结构、LLM、会员规则或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-01-llm-transport-20260801`;检查点为
|
||||
`xiaobai-reduction-02-http-dispatch-20260801`。
|
||||
|
||||
## CR-03验收口径
|
||||
|
||||
- 图表模块不再定义第二份市场后缀转换函数,仍保留原局部名称和两个调用点。
|
||||
- 深市、沪市、北交所的既有映射结果保持不变;不借本批修正或扩展代码规则。
|
||||
- 原图表文件除该函数外的所有顶层定义继续与原版AST逐项相等。
|
||||
- 原版`_stock_market_code`函数的参数和函数体必须与唯一共享实现AST相等,运行时别名必须指向
|
||||
同一个函数对象。
|
||||
|
||||
## CR-03结果
|
||||
|
||||
- 删除`backend/features/market/charts.py`中第二份10行定义,以1行导入别名复用共享实现,生产代码
|
||||
净减少9行;全仓后端只剩一份沪深京后缀转换函数体。
|
||||
- 未合并实时聚合、东方财富图表、iFinD和Tushare的HTTP传输;它们的缓存、错误、重试和降级语义
|
||||
不同,仅有外形相似,证据不足以安全抽象。
|
||||
- 原有迁移期整文件相等断言被等价范围断言、共享函数AST断言和唯一对象断言替代,没有降低门禁。
|
||||
- 原版231项、候选317项、纯`app/`导出254项、45项Playwright通过;API/架构注册表、
|
||||
24个JavaScript文件、Git空白检查和SQLite完整性检查通过。
|
||||
- 本批不修改图表请求、数据来源、缓存、时间范围、行情计算、前端、CSS、数据库或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-02-http-dispatch-20260801`;检查点为
|
||||
`xiaobai-reduction-03-market-symbol-20260801`。
|
||||
|
||||
## CR-04验收口径
|
||||
|
||||
- 只合并参数、函数体和运行结果完全一致的数值转换函数,不借本批改变任何业务计算或异常默认值。
|
||||
- `finite_number`继续拒绝`NaN`与正负无穷;`non_nan_number`继续只拒绝`NaN`并保留正负无穷。
|
||||
- Tushare与智能选股必须复用有限数策略;市场洞察与情绪引擎必须复用非NaN策略,并继续暴露原局部
|
||||
`_number`名称以保持兼容。
|
||||
- 实时行情和图表转换器的空值、默认值或参数签名语义不同,必须继续独立保留,不能因名称相同而合并。
|
||||
- 原版函数参数和函数体分别与共享实现AST相等;四个消费者的局部别名必须指向对应的唯一函数对象。
|
||||
|
||||
## CR-04结果
|
||||
|
||||
- 删除Tushare、智能选股、市场洞察和情绪引擎中的四份重复函数体,新建两种明确命名的共享策略;生产
|
||||
代码净减少约12行,全仓AST扫描不再发现完全相同的函数定义。
|
||||
- 将可复用的函数及模块AST契约归入测试辅助层,原有迁移保持性测试改为“未改范围保持相等、被替换
|
||||
函数与共享实现相等、运行时唯一对象”三重断言,没有降低门禁。
|
||||
- 实时行情`backend/data/realtime.py::_number`与图表`backend/features/market/charts.py::_number`
|
||||
被明确保留;它们不是本批重复实现,也未改变行为。
|
||||
- 58项定向测试、41项保持性/治理测试、原版231项、候选320项、纯`app/`导出257项和45项
|
||||
Playwright通过;24个JavaScript文件、API/架构注册表、Git空白检查和SQLite完整性检查通过。
|
||||
- 本批不修改前端、CSS、接口、数据来源、行情口径、选股条件、数据库、LLM、权限或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-03-market-symbol-20260801`;检查点为
|
||||
`xiaobai-reduction-04-numeric-normalization-20260801`。
|
||||
|
||||
## CR-05验收口径
|
||||
|
||||
- 逐项扫描根级Python入口、生产代码、测试、工具和动态导入;没有消费者或兼容责任的入口才能删除。
|
||||
- 规范后端不得经由`screener`、`advanced_strategies`、`tushare_client`或`server`兼容入口
|
||||
间接访问已经归位的实现。
|
||||
- 所有根级模块继续保持原导入名称、导出对象及模块对象身份,既有启动命令和第三方维护脚本不受影响。
|
||||
- `api_access`、选股Repository的惰性`sentiment_engine`导入及根级`database.py`属于已登记边界,
|
||||
分别留到HTTP、Repository阶段处理,不在本批跨边界修改。
|
||||
|
||||
## CR-05结果
|
||||
|
||||
- 审计确认21个根级兼容入口均有测试、工具、启动或原公开导入契约消费者,因此本批没有冒险删除文件。
|
||||
- 容器、策略编译器、选股引擎及数据同步命令的五处导入改为规范模块路径,正式代码不再通过四个根级
|
||||
兼容模块反向进入实现;运行代码行数未增加。
|
||||
- 特性边界测试取消选股引擎旧例外,并新增全后端兼容导入门禁;只允许两项已登记过渡边界,后续代码
|
||||
无法重新引入隐式根级依赖。
|
||||
- 候选321项、纯`app/`导出258项、24个JavaScript文件、API/架构注册表、Git空白检查和SQLite
|
||||
完整性检查通过;本批不涉及页面、CSS或浏览器行为。
|
||||
- 本批不修改业务计算、策略公式、数据源、API、数据库、LLM、权限、前端或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-04-numeric-normalization-20260801`;检查点为
|
||||
`xiaobai-reduction-05-compatibility-boundaries-20260801`。
|
||||
|
||||
## CR-06验收口径
|
||||
|
||||
- 只合并参数、函数体和异常行为完全相同的日期/文本转换;名称相似但空值、未来日期、错误文案或
|
||||
输入格式不同的函数不得合并。
|
||||
- Tushare与选股引擎继续暴露局部`_display_date`名称,并分别指向唯一共享实现。
|
||||
- 原版两个`_display_date`函数必须分别与共享实现AST相等,所有原调用结果保持不变。
|
||||
- 市场洞察的日期显示函数会清理连字符并容忍空值,语义不同,必须继续独立保留。
|
||||
|
||||
## CR-06结果
|
||||
|
||||
- 删除Tushare与选股引擎内两个重复日期函数体,新增`display_compact_date`唯一策略;生产代码总
|
||||
行数不增加,重复函数体由两份降为一份。
|
||||
- 架构清单登记日期格式唯一所有权;保持性测试改为未改范围AST相等、共享函数AST相等和运行时
|
||||
对象身份三重契约,没有放宽原迁移门禁。
|
||||
- `normalize_date`、市场洞察日期显示、实时行情时间格式和会员日期边界因语义不同均原样保留。
|
||||
- 候选321项、纯`app/`导出258项、24个JavaScript文件、API/架构注册表、Git空白检查和SQLite
|
||||
完整性检查通过;本批不涉及页面、CSS或浏览器行为。
|
||||
- 本批不修改日期输入规则、业务计算、选股结果、接口、数据库、数据源、LLM、权限或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-05-compatibility-boundaries-20260801`;检查点为
|
||||
`xiaobai-reduction-06-date-formatting-20260801`。
|
||||
|
||||
## CR-07验收口径
|
||||
|
||||
- iFinD、图表、实时观察器和Provider适配器必须只在`build_data_gateway`创建,并由容器共享。
|
||||
- Tushare必须继续通过实时Token供应器按需创建,不能为了减少对象数量缓存过期Token。
|
||||
- 市场服务中为原版隔离测试桩保留的一处`TushareClient(self.token)`是明确兼容例外,不得被误判为
|
||||
第二条正式数据链路。
|
||||
- 不得合并Tushare、iFinD、东方财富和腾讯的传输、缓存、重试或降级逻辑。
|
||||
|
||||
## CR-07结果
|
||||
|
||||
- 全后端构造点扫描确认iFinD、MarketChart、东方财富图表和实时观察器均只有网关一个创建位置;
|
||||
`ApplicationContainer`暴露的是同一对象引用,没有第二份客户端。
|
||||
- Tushare Provider使用动态Token供应器,市场服务只有一处已登记测试兼容回退;本批没有发现可安全
|
||||
删除的生产实现,因此不为追求行数强行修改运行代码。
|
||||
- 架构清单新增Provider创建所有权,自动测试会在未来出现第二个未登记构造点时失败。
|
||||
- 候选322项、纯`app/`导出259项、24个JavaScript文件、API/架构注册表、Git空白检查和SQLite
|
||||
完整性检查通过;本批不涉及页面、CSS或浏览器行为。
|
||||
- 本批不修改请求频率、缓存、重试、Token更新、数据源选择、计算口径、API、数据库或前端。
|
||||
|
||||
本批基线为`xiaobai-reduction-06-date-formatting-20260801`;检查点为
|
||||
`xiaobai-reduction-07-provider-ownership-20260801`。
|
||||
|
||||
## CR-08验收口径
|
||||
|
||||
- 只合并NDJSON响应头、事件序列化、完成事件、业务错误事件、客户端断线和连接关闭这些传输行为。
|
||||
- 问师继续直接输出原事件字典;复盘助手继续把文本分片包装为`delta/content`事件。
|
||||
- 两个功能各自的业务异常类型、请求体错误状态码、错误正文和流创建时机保持不变。
|
||||
- `_write_stream_event`与连接生命周期必须只由`backend/http/handler.py`拥有,应用大类和功能HTTP
|
||||
模块不再保留第二份实现。
|
||||
|
||||
## CR-08结果
|
||||
|
||||
- 删除问师和复盘助手各16行重复流式控制流,并将应用大类中的10行事件写入方法归入HTTP共享层;
|
||||
新共享实现29行、两个调用适配共3行,生产代码净减少10行。
|
||||
- 新增专项测试固定四个响应头、中文NDJSON序列化、增量顺序、完成事件、业务错误事件和关闭状态。
|
||||
- 候选324项、纯`app/`导出261项及45项Playwright通过;24个JavaScript文件、API/架构注册表、
|
||||
Git空白检查和SQLite完整性检查通过。
|
||||
- 本批不修改提示词、模型选择、会员计次、流式正文、前端解析、API路径、数据库或数据源。
|
||||
|
||||
本批基线为`xiaobai-reduction-07-provider-ownership-20260801`;检查点为
|
||||
`xiaobai-reduction-08-ndjson-transport-20260801`。
|
||||
|
||||
## CR-09验收口径
|
||||
|
||||
- 只有消费者全部属于单一领域、且能够按原函数体机械移动的方法才从应用门面移出。
|
||||
- `_ifind_field`与`_ifind_row_code`继续保持静态/类方法签名、字段优先级、大小写规则和代码正则。
|
||||
- 账号委托属于稳定公开门面;系统设置属于跨领域协调;后台刷新留到CR-11,本批均不得删除或重写。
|
||||
- 移动后`DashboardService`必须继续通过Mixin解析同名方法,调用点和返回值不变。
|
||||
|
||||
## CR-09结果
|
||||
|
||||
- 将iFinD字段匹配和股票代码提取两个方法从应用大类机械移动到股池服务,原版与迁移方法AST逐项
|
||||
相等;应用大类不再直接拥有股池专属实现。
|
||||
- 连同迁移期遗留空行,`backend/application.py`减少32行,股池服务增加24行,生产代码净减少8行。
|
||||
- 候选324项、纯`app/`导出261项、24个JavaScript文件、API/架构注册表、Git空白检查和SQLite
|
||||
完整性检查通过;本批不涉及页面、CSS或浏览器行为。
|
||||
- 本批不修改字段匹配、涨跌停原因补全、接口、数据源、缓存、数据库、权限或前端。
|
||||
|
||||
本批基线为`xiaobai-reduction-08-ndjson-transport-20260801`;检查点为
|
||||
`xiaobai-reduction-09-service-facade-20260801`。
|
||||
|
||||
## CR-10验收口径
|
||||
|
||||
- 只移动调用方、数据表和业务含义均明确属于单一领域的方法;数据库连接、事务和返回值必须保持不变。
|
||||
- `list_sector_phase_overrides`、`save_sector_phase_override`与`delete_sector_phase_override`必须由
|
||||
`backend/features/heaven/repository.py`拥有,并继续通过`ReviewDatabase`的Mixin解析。
|
||||
- 不修改表结构、迁移顺序、时间格式、排序、冲突更新或删除结果语义。
|
||||
- `wencai_saved_queries`及其三个方法属于已登记的账户隔离兼容数据;即使前端入口已取消,也必须保留。
|
||||
|
||||
## CR-10结果
|
||||
|
||||
- 将五行行业阶段覆盖的查询、保存和删除三个方法从根级`database.py`机械移动到问天Repository;
|
||||
调用名称、SQL、事务边界、时间值和返回结果均未改变。
|
||||
- 根级数据库门面不再直接拥有问天领域的持久化实现,问财历史兼容表和方法完整保留,未扩大删除范围。
|
||||
- 34项Repository、问天、账户隔离、清理契约及迁移定向测试通过;候选324项、纯`app/`导出261项、
|
||||
24个JavaScript文件、API/架构注册表、Git空白检查和SQLite完整性检查通过。
|
||||
- 本批不修改页面、CSS、API、数据库结构、行情、数据源、业务计算、LLM、权限或后台任务。
|
||||
|
||||
本批基线为`xiaobai-reduction-09-service-facade-20260801`;检查点为
|
||||
`xiaobai-reduction-10-repository-ownership-20260801`。
|
||||
|
||||
## CR-11验收口径
|
||||
|
||||
- 导入`backend.application`或构造`DashboardService`不得启动后台调度;必须先成功绑定HTTP端口,
|
||||
再由运行时显式启动。
|
||||
- 同一`InProcessJobRunner`重复启动调度器必须返回同一活动线程,停止必须置位并在限定时间内等待退出,
|
||||
有序停止后允许重新启动。
|
||||
- 运行时关闭顺序固定为:停止调度、等待已提交任务、关闭HTTP服务器;迁移对比工具继续兼容原版入口。
|
||||
- 三个任务的注册定义、5秒刷新频率、3秒初始延迟、幂等键、锁、重试、业务函数和结果不得改变。
|
||||
- 声明的超时继续是目标与审计字段;Python线程不能安全强杀,本批不伪造硬取消能力。
|
||||
|
||||
## CR-11结果
|
||||
|
||||
- 删除`DashboardService`构造阶段的调度副作用,端口占用、模块导入和单元测试不再提前创建后台写线程;
|
||||
`backend/bootstrap/runtime.py`成为正式启动与停止所有者。
|
||||
- `InProcessJobRunner`集中持有调度停止事件和线程引用;重复启动幂等,停止可等待,原任务锁、持久化运行
|
||||
状态、成功幂等、失败记录和后续重试逻辑保持不变。
|
||||
- 新增语法树与运行顺序门禁,固定“构造不启动”“绑定后启动”“停止后关服”,并补齐重复启动与重启测试。
|
||||
- 候选328项、纯`app/`导出265项和45项Playwright通过;24个JavaScript文件、API/架构注册表、
|
||||
Git空白检查和SQLite完整性检查通过。
|
||||
- 本批没有可安全删除的重复任务实现;为补齐原先缺失的生命周期,生产代码净增加24行。增加内容仅为
|
||||
调度状态、幂等启停和运行时委托,不新增业务层、任务或兼容包装。
|
||||
- 本批不修改页面、CSS、API、数据源、刷新计算、自动选股条件、数据库结构、权限或LLM。
|
||||
|
||||
本批基线为`xiaobai-reduction-10-repository-ownership-20260801`;检查点为
|
||||
`xiaobai-reduction-11-background-jobs-20260801`。
|
||||
|
||||
## CR-11人工验收修正
|
||||
|
||||
- 2026-08-02人工验收发现本地页面可访问,但行情与LLM同时无法连接。第一原因是验收服务由受限
|
||||
自动化会话启动,子进程继承了禁止外部网络访问的权限;重新在主机正常网络权限下启动后恢复。
|
||||
- 随后的主模型连接测试暴露`LLMServiceMixin`机械迁移时遗漏`validate_text`导入,导致请求在真正
|
||||
访问模型前抛出`NameError`并关闭HTTP连接;恢复原依赖并增加保存模型连接探测的运行契约测试。
|
||||
- 网站自身实测`000001`返回Tushare日K 60根、分时242点;主模型
|
||||
`MiniMax-M2.7-highspeed`在2236毫秒内回复`OK`,证明服务进程的数据与LLM出网链路均已恢复。
|
||||
- 修正后候选329项、纯`app/`导出266项通过;本次只恢复缺失导入和测试,不修改模型配置、额度、
|
||||
提示词、回退策略、行情来源或计算逻辑。
|
||||
- 继续验收观势时发现模型已经成功返回,但问天服务在保存解势记录前关闭了HTTP连接。原因是原版
|
||||
`server.py`已有的`secrets`导入在机械拆分到问天服务时遗漏,生成非观气记录去重键时触发
|
||||
`NameError`。迁移版恢复该标准库依赖,并增加“模型成功返回后保存观势结果”的完整服务回归测试。
|
||||
- 使用`000001 平安银行`完成真实页面复测:六爻安全门6/6通过,解势结果正常返回并写入历史,
|
||||
`8797`错误日志为空。该修正不改变提示词、模型选择、额度、卦象计算、记录结构或前端行为。
|
||||
|
||||
本修正基线为`xiaobai-reduction-11-background-jobs-20260801`;检查点为
|
||||
`xiaobai-reduction-11-runtime-connectivity-fix-20260802`;后续解势修正检查点为
|
||||
`xiaobai-reduction-11-heaven-interpret-fix-20260802`。
|
||||
|
||||
## CR-12验收口径
|
||||
|
||||
- 本批只处理不同样式文件中、同为顶层、选择器与完整声明逐字等价的规则;媒体查询、伪状态、
|
||||
动画、问天隔离样式以及仅外形相似的规则不进入删除范围。
|
||||
- 删除的必须是较早加载的副本,较晚层继续提供完全相同的最终声明;样式加载顺序、变量、HTML、
|
||||
JavaScript和主题切换逻辑均不改变。
|
||||
- 迁移保真测试必须逐段登记允许退休的原始CSS文本,除登记片段外,其余源码继续与原版逐字符相等。
|
||||
- 浏览器必须验证跨层顶层精确重复为零,并通过日间、夜间、桌面、390px移动端及全站交互回归。
|
||||
|
||||
## CR-12结果
|
||||
|
||||
- 通过浏览器CSSOM扫描七层运行样式,共发现57组完整重复规则;本批只批准其中7组跨文件顶层重复,
|
||||
分别涉及工作区显示、折叠侧栏、板块轮动末列、选股概率值、摘要条两项声明及龙虎榜原因列。
|
||||
其余50组位于同文件或嵌套媒体条件等更复杂环境,证据不足,继续保留。
|
||||
- 删除7条较早加载的规则,三个生产CSS文件净减少19行、480字节;后层最终规则、选择器优先级与
|
||||
加载顺序均未改变,没有新增兼容覆盖或第二套样式实现。
|
||||
- 新增浏览器CSSOM门禁,任何两个样式层再次出现相同顶层选择器与完整声明都会失败;迁移保真门禁
|
||||
只允许已登记的7个精确源码片段退休,其他CSS差异仍会失败。
|
||||
- 真实`8797`页面复核情绪周期日间/夜间、龙虎榜和390×844移动端;三个受影响节点的计算样式
|
||||
与删除前一致,移动端无横向溢出。候选330项、CSS/前端契约33项、迁移对照63项及46项
|
||||
Playwright全部通过,24个JavaScript文件、API/架构注册表和SQLite完整性检查通过。
|
||||
- 本批不修改页面布局、颜色、字体、间距、响应式规则、主题、动画、业务功能、API、数据库或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-11-heaven-interpret-fix-20260802`;检查点为
|
||||
`xiaobai-reduction-12-css-exact-duplicates-20260802`。
|
||||
|
||||
## CR-13验收口径
|
||||
|
||||
- 本批只处理不同样式文件中、处于浏览器规范化后完全相同媒体条件下、选择器与完整CSSOM声明完全
|
||||
相同的规则;不同媒体上下文、同文件重复、近似声明、动画和问天隔离样式继续保留。
|
||||
- 删除的必须是较早加载的副本,较晚样式层继续提供相同声明;媒体条件、规则顺序、选择器优先级、
|
||||
变量、HTML、JavaScript和主题逻辑不得改变。
|
||||
- 保真门禁必须逐段登记允许退休的原始源码;浏览器门禁必须递归遍历嵌套规则,并只将同一上下文内
|
||||
跨文件的完整重复判为失败。
|
||||
- 390×844明暗主题、1000×800中等宽度和1000×600低高度断点必须保持原计算样式与视觉结果。
|
||||
|
||||
## CR-13结果
|
||||
|
||||
- 浏览器CSSOM确认22组跨文件嵌套重复:1组位于721-1279px媒体条件,12组位于720px移动端条件,
|
||||
9组位于720px或1023px低高度复合条件;全部删除较早层副本,后层规则原样保留。
|
||||
- `styles.css`减少65行,`redesign-v2.css`减少15行,生产CSS合计净减少80行、约1.9 KB;没有新增
|
||||
兼容覆盖、声明值、选择器或样式文件。
|
||||
- Playwright门禁由顶层扫描扩展为递归上下文扫描,修改后同一嵌套上下文的跨文件完整重复为0;同文件
|
||||
重复和不同上下文规则不在本批范围,未被误删。
|
||||
- 1000×800和1000×600修改前后截图逐字节一致;390×844明暗主题的关键显示、定位、间距、网格、
|
||||
溢出和导航状态一致,页面目视无差异。
|
||||
- 候选330项、CSS/前端契约33项、迁移对照63项及46项Playwright全部通过;24个JavaScript文件、
|
||||
API/架构注册表和SQLite完整性检查通过。
|
||||
- 本批不修改页面布局、颜色、字体、间距、主题、动画、业务功能、API、数据库、数据源、LLM或部署。
|
||||
|
||||
本批基线为`xiaobai-reduction-12-css-exact-duplicates-20260802`;检查点为
|
||||
`xiaobai-reduction-13-css-nested-duplicates-20260802`。
|
||||
|
||||
## CR-13后续产品修正:龙虎榜整页滚动
|
||||
|
||||
- 2026-08-02用户明确要求取消龙虎榜“当日操作明细单独纵向滚动”,改为龙虎榜主内容区整页纵向滚动,
|
||||
解决低分辨率下操作明细可视高度过小的问题;这是经批准的产品行为变化,不作为CSS去重处理。
|
||||
- 龙虎榜从桌面固定视口共享规则中独立出来;主内容区继续使用工作区高度并承担纵向滚动,游资卡片、
|
||||
操作明细和待归类席位按内容自然展开,宽操作表继续保留横向滚动。
|
||||
- 保真门禁以精确源码替换单独登记本次差异,其他CSS仍与原母版逐字符比较;未新增覆盖层或第二套规则。
|
||||
- 1366×768真实页面中主内容区为692px、内容高度为2620px,整页滚动可达;操作明细自身高度与内容
|
||||
高度一致,不再形成纵向小窗口,1180px宽表仍可横向滚动。
|
||||
- 本次不修改龙虎榜数据、筛选、搜索、游资卡牌、表格字段、游资档案、API、数据库或其他页面的
|
||||
滚动所有权。
|
||||
|
||||
本修正基线为`xiaobai-reduction-13-css-nested-duplicates-20260802`;检查点为
|
||||
`xiaobai-fix-dragon-page-scroll-20260802`。
|
||||
|
||||
## CR-14验收口径
|
||||
|
||||
- 只处理同一CSS文件、同一浏览器规范化媒体上下文中,选择器和完整CSSOM声明完全相同的规则;
|
||||
不同媒体上下文、近似声明、动画和问天隔离样式继续保留。
|
||||
- 每组只删除较早出现的副本并保留最后一份原规则;样式文件加载顺序、媒体条件、选择器优先级、变量、
|
||||
HTML、JavaScript和主题逻辑均不得改变。
|
||||
- 保真门禁必须精确登记原始片段及其出现/退休次数;浏览器门禁从“只拒绝跨文件重复”提升为
|
||||
“同一上下文内任何完整重复均拒绝”。
|
||||
- 1366×768桌面暗色关键页面和390×844移动端关键页面的尺寸、滚动范围及视觉结果必须保持不变,
|
||||
并通过全站Playwright回归。
|
||||
|
||||
## CR-14结果
|
||||
|
||||
- 浏览器CSSOM确认33组同文件精确重复,其中两个规则各出现三次;共退休35个较早副本:
|
||||
`styles.css`9个、`renovation.css`25个、`redesign-v2.css`1个,运行时同上下文完整重复降为0。
|
||||
- 三个生产CSS文件合计净减少97行、3272字节;未新增选择器、声明、覆盖层或样式文件,最后一份原规则
|
||||
及其媒体上下文全部保留。
|
||||
- 保真门禁新增精确出现次数与退休次数审计,除登记片段外继续与原母版逐字节比较;Playwright CSSOM
|
||||
门禁现会拒绝跨文件和同文件重复,后续不能重新堆回同类规则。
|
||||
- 1366×768暗色模式复核情绪周期、集合竞价、题材库、智能选股、问师和我的复盘;390×844复核
|
||||
情绪周期、集合竞价、智能选股和我的复盘。关键尺寸、滚动范围保持一致,移动端情绪周期截图逐像素一致,
|
||||
其余页面目视无差异且无横向溢出。
|
||||
- 候选330项、CSS/前端契约33项、迁移对照63项及46项Playwright全部通过;24个JavaScript文件、
|
||||
API/架构注册表和SQLite完整性检查通过。
|
||||
- 本批不修改页面布局、颜色、字体、间距、响应式行为、主题、动画、业务功能、API、数据库、数据源、
|
||||
LLM或部署。
|
||||
|
||||
本批基线为`xiaobai-fix-dragon-page-scroll-20260802`;检查点为
|
||||
`xiaobai-reduction-14-css-same-file-duplicates-20260802`。
|
||||
|
||||
## 人工验收记录
|
||||
|
||||
- 2026-08-01:用户检查CR-02与CR-03运行结果,确认未发现明显异常。本记录仅表示本轮可见功能与
|
||||
页面使用未发现明显回归,不替代后续批次各自的自动测试和人工抽查。
|
||||
@@ -0,0 +1,60 @@
|
||||
# Stage 01: Governance Baseline
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
Baseline commit: `0030bb8cc18c5aa7107dd8fd866355a6507bd6da`
|
||||
|
||||
## Purpose
|
||||
|
||||
This baseline freezes observable behavior before architecture governance begins. Later stages
|
||||
may move code and introduce internal contracts, but must not change API behavior, account
|
||||
boundaries, persisted user data, desktop presentation, or existing workflows unless a change
|
||||
is approved separately.
|
||||
|
||||
## Deployment Contract
|
||||
|
||||
- One Python process serves the API and static browser client.
|
||||
- One SQLite database is persisted at `data/review.db`.
|
||||
- Docker exposes `0.0.0.0:8765` and mounts only `./data` at `/app/data`.
|
||||
- Public market data is shared; private records are scoped by `user_id`.
|
||||
- Market-data and LLM credentials remain server-side.
|
||||
|
||||
## Verified Baseline
|
||||
|
||||
- Python: 169 tests passed.
|
||||
- Playwright: 45 tests passed at desktop and mobile viewports.
|
||||
- JavaScript entry points pass `node --check`.
|
||||
- `git diff --check` reports no patch errors.
|
||||
- SQLite `PRAGMA integrity_check` returns `ok`.
|
||||
- The database was backed up with the SQLite backup API to
|
||||
`data/backups/governance-stage-01-0030bb8.db` and the backup independently passed
|
||||
`PRAGMA integrity_check`. The backup is intentionally excluded from Git.
|
||||
|
||||
## Repeatable Verification
|
||||
|
||||
Run the fast baseline on every structural change:
|
||||
|
||||
```shell
|
||||
python tools/verify_baseline.py
|
||||
```
|
||||
|
||||
Run the browser suite at phase boundaries:
|
||||
|
||||
```shell
|
||||
python tools/verify_baseline.py --e2e
|
||||
```
|
||||
|
||||
## Regression Gates
|
||||
|
||||
1. API routes and response fields remain compatible until a versioned contract says otherwise.
|
||||
2. Existing SQLite files must upgrade without deleting or reassigning user-owned rows.
|
||||
3. A failed external data request must not synthesize market prices.
|
||||
4. Ordinary, member, and administrator permissions must remain distinct.
|
||||
5. Desktop visual refactors require 1080P and 4K comparison in both themes.
|
||||
6. Mobile work is isolated from the approved desktop shell and workflows.
|
||||
7. Old code is deleted only after its replacement is active and reference scans are clean.
|
||||
|
||||
## Rollback
|
||||
|
||||
Code can return to this point with Git commit `0030bb8`. Database rollback must use the
|
||||
matching backup above; a code rollback alone is not sufficient after a schema migration.
|
||||
@@ -0,0 +1,72 @@
|
||||
# Stage 02: Architecture Inventory
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
The machine-readable inventory is `architecture-inventory.json`. Regenerate it with:
|
||||
|
||||
```shell
|
||||
python tools/build_architecture_inventory.py
|
||||
python tools/build_architecture_inventory.py --check
|
||||
```
|
||||
|
||||
## Current Shape
|
||||
|
||||
- The product is a single-container modular monolith with one Python process and SQLite WAL.
|
||||
- The browser is a build-free single-page application served by the same process.
|
||||
- The primary navigation exposes 16 workspaces.
|
||||
- The current database bootstrap defines 34 tables.
|
||||
- HTTP routing and service orchestration are concentrated in `server.py`.
|
||||
- Schema creation, inline migrations, and all persistence methods are concentrated in
|
||||
`database.py`.
|
||||
- The frontend loads six ordered CSS layers and one global application script.
|
||||
- Background refresh and automatic screener work execute as daemon threads in the HTTP
|
||||
process.
|
||||
|
||||
## Ownership Inventory
|
||||
|
||||
### Shared market and system data
|
||||
|
||||
Dashboard snapshots, synchronization runs, stock master data, daily bars, benchmark bars,
|
||||
daily indicators, fundamental indicators, money flow, auction factors, earnings events,
|
||||
popularity factors, institutional list data, public screener strategies, reason overrides,
|
||||
seat aliases, sector phase overrides, and encrypted system settings are shared.
|
||||
|
||||
### User-owned data
|
||||
|
||||
Sessions, personal credentials, birth profiles, watchlists, review notes, custom strategies,
|
||||
private screener runs, mentor messages and preferences, saved queries, tracking entries,
|
||||
alerts, trades, assistant messages, and heaven readings are user-scoped. Every repository
|
||||
extracted in later stages must preserve this boundary explicitly.
|
||||
|
||||
## Runtime Entrypoints
|
||||
|
||||
- HTTP and application bootstrap: `server.py`
|
||||
- Persistence and migrations: `database.py`
|
||||
- Access policy: `api_access.py`
|
||||
- Market providers: `tushare_client.py`, `ifind_client.py`, `chart_data_provider.py`,
|
||||
`realtime_aggregator.py`
|
||||
- Deterministic computation: `sentiment_engine.py`, `screener.py`, `advanced_strategies.py`,
|
||||
`market_insights.py`, `heaven_engine.py`
|
||||
- LLM calls: `mentor_agent.py`, `heaven_agent.py`, `assistant_agent.py`, `llm_strategy.py`
|
||||
- Browser shell and pages: `static/index.html`, `static/app.js`, and the ordered CSS files
|
||||
|
||||
## Primary Structural Risks
|
||||
|
||||
1. Request routing, use cases, scheduling, provider selection, serialization, and account
|
||||
context coexist in one service module.
|
||||
2. Database migrations cannot be reviewed independently from repository behavior.
|
||||
3. Several business paths instantiate provider clients directly, so source policy is not
|
||||
centrally enforceable.
|
||||
4. LLM authorization, quota accounting, model selection, streaming, and error handling are
|
||||
distributed across multiple adapters.
|
||||
5. Global DOM state and page behavior share one JavaScript file, increasing cross-page
|
||||
regression risk.
|
||||
6. CSS correctness depends on load order and late overrides rather than explicit layer
|
||||
ownership.
|
||||
7. In-process daemon jobs have no durable queue and some failure paths are not observable.
|
||||
|
||||
## Inventory Limits
|
||||
|
||||
The JSON inventory records source-level declarations and references. It does not claim that
|
||||
every declared endpoint is exercised or every table is populated. Runtime coverage and data
|
||||
quality are separate contracts introduced in later governance stages.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Stage 04: Governance Registries
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
Four versioned registries now describe the existing product before runtime decomposition:
|
||||
|
||||
1. `config/pages.config.json` registers all 16 primary workspaces.
|
||||
2. `config/features.config.json` registers 20 feature owners and their access/data scope.
|
||||
3. `config/api.config.json` registers 74 method/path combinations, including dynamic paths.
|
||||
4. `config/data-fields.config.json` registers 18 initial canonical data products and provider
|
||||
eligibility.
|
||||
|
||||
The API registry is generated from the current request handler so route drift fails tests. The
|
||||
other registries are curated contracts and may change only through an explicit product or data
|
||||
governance decision.
|
||||
|
||||
## Access Decisions Preserved
|
||||
|
||||
- Health, login, and registration are public.
|
||||
- Market views require an authenticated account.
|
||||
- Intelligent screening, Mentor, Wentian, and their history endpoints require membership.
|
||||
- My Review remains visible to authenticated users; LLM assistant endpoints remain member
|
||||
gated inside that workspace.
|
||||
- System management and shared knowledge modification remain administrator-only.
|
||||
- Frontend visibility does not grant API access.
|
||||
|
||||
## Data Decisions Preserved
|
||||
|
||||
- Tushare and licensed iFinD data may be calculation inputs when a dataset contract permits it.
|
||||
- Eastmoney and Tencent public endpoints are display-only in the initial registry.
|
||||
- Analyst consensus history and Level-2 microstructure remain blocked rather than silently
|
||||
approximated.
|
||||
- Chart fallbacks remain separate from calculation datasets.
|
||||
- The current unadjusted deterministic daily-bar behavior is recorded honestly; adjustment
|
||||
normalization is deferred to the data-quality phase rather than changed here.
|
||||
|
||||
## Transitional Rule
|
||||
|
||||
These files do not yet replace `server.py`, `api_access.py`, or the current frontend navigation.
|
||||
They are checked against those surfaces to prevent untracked drift. Later phases make each
|
||||
registry authoritative in one atomic migration with compatibility tests.
|
||||
|
||||
## Verification
|
||||
|
||||
`tests/test_governance_registries.py` verifies uniqueness, page coverage, current API coverage,
|
||||
explicit public routes, feature ownership, role validity, and provider eligibility.
|
||||
@@ -0,0 +1,24 @@
|
||||
# Stage 05: Bootstrap and Dependency Assembly
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Environment and legacy LLM credential resolution moved to `backend/bootstrap/settings.py`.
|
||||
- Encryption-key initialization remains behavior-compatible and server-side.
|
||||
- Stable service construction moved to `backend/bootstrap/container.py`.
|
||||
- `DashboardService` keeps its compatibility attributes but receives them from one application
|
||||
container.
|
||||
- The iFinD client is instantiated once and shared by chart services.
|
||||
- HTTP routes, API payloads, background thread timing, database paths, and frontend assets are
|
||||
unchanged.
|
||||
|
||||
## Transitional Boundary
|
||||
|
||||
Some methods still construct Tushare clients directly. Stage 06 introduces `DataGateway` and
|
||||
migrates those provider creation paths without combining that work with bootstrap changes.
|
||||
|
||||
## Rollback
|
||||
|
||||
Reverting this stage restores inline service construction. No database or configuration
|
||||
migration is required.
|
||||
@@ -0,0 +1,29 @@
|
||||
# Stage 06: Unified Data Gateway
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Added canonical provider and dataset contracts under `backend/data`.
|
||||
- Added `DataSourcePolicy`, loaded from the Stage 04 data registry.
|
||||
- Added provider adapters for Tushare and iFinD.
|
||||
- Added one `DataGateway` that owns Tushare creation, the shared iFinD client, chart routing,
|
||||
and isolated Eastmoney/Tencent realtime observation.
|
||||
- Replaced all real `DashboardService` Tushare construction paths with the gateway.
|
||||
- Kept one compatibility constructor for unit tests that instantiate an incomplete service with
|
||||
`__new__`; production instances never use it.
|
||||
- The Tushare token is supplied lazily, so administrator credential changes do not leave a
|
||||
stale client in memory.
|
||||
|
||||
## Enforcement Introduced
|
||||
|
||||
- Unregistered datasets fail.
|
||||
- Blocked datasets fail.
|
||||
- Public-web providers cannot be promoted to calculation inputs through a fallback call.
|
||||
- Display chart fallbacks remain distinct from deterministic calculation datasets.
|
||||
|
||||
## Deferred to Stage 07
|
||||
|
||||
Stage 06 centralizes provider access but does not yet attach freshness, coverage, unit, and
|
||||
point-in-time quality evidence to every returned observation. Stage 07 introduces those gates
|
||||
without changing provider routing again.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Stage 07: Data Quality and Provenance Gates
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Added a quality rule for every registered data product.
|
||||
- Added canonical Asia/Shanghai timestamp handling.
|
||||
- Added source and usage verification before quality acceptance.
|
||||
- Added maximum-age checks for realtime auction, intraday charts, and realtime indices.
|
||||
- Added minimum coverage thresholds for deterministic datasets.
|
||||
- Added unit profiles for prices, shares, currency, percentages, ratios, ranks, and timestamps.
|
||||
- Added adjustment checks that distinguish current unadjusted deterministic bars from iFinD
|
||||
forward-adjusted display charts.
|
||||
- Added announcement-date point-in-time checks for financial data.
|
||||
- Added explicit provider chains; a display fallback cannot silently become a calculation
|
||||
source.
|
||||
- Analyst consensus and Level-2 remain blocked until a qualified provider is registered.
|
||||
|
||||
## Fail-Closed Contract
|
||||
|
||||
A calculation evidence envelope is rejected when its source is unauthorized, its dataset is
|
||||
blocked, its timestamp is from the future, its realtime data is stale, its coverage is below
|
||||
the registered threshold, its units or adjustment mode differ, or its financial record was not
|
||||
available at the evaluation time.
|
||||
|
||||
## Compatibility
|
||||
|
||||
Existing legacy provider response shapes remain unchanged in this stage. New governed feature
|
||||
paths must submit quality evidence through `DataGateway.require_quality`. Existing feature
|
||||
paths receive envelopes as they migrate behind domain services, avoiding a simultaneous
|
||||
rewrite of calculations and provider routing.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Stage 08: Database Connection and Migration Foundation
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Centralized SQLite connection policy in `SQLiteConnectionFactory`.
|
||||
- Preserved WAL, foreign-key enforcement, row mapping, handle cleanup, and the existing
|
||||
20-second contention tolerance.
|
||||
- Added an ordered migration runner with immutable checksums and an applied-migration ledger.
|
||||
- Added savepoint rollback so a failed migration cannot be recorded or leave partial schema.
|
||||
- Adopted existing databases as version `0001` only after verifying the required legacy
|
||||
tables.
|
||||
- Kept the legacy idempotent bootstrap in place for compatibility with databases created by
|
||||
every previous application version.
|
||||
|
||||
## Forward Rule
|
||||
|
||||
All schema changes after this stage must be a new immutable module under
|
||||
`backend/database/migrations`. Editing an applied migration is rejected by checksum. A
|
||||
database containing a migration unknown to the running code is rejected rather than opened
|
||||
with an older schema interpretation.
|
||||
|
||||
## Residual Risk
|
||||
|
||||
The historical inline bootstrap remains a compatibility facade during repository migration.
|
||||
It may be removed only after legacy upgrade fixtures cover every supported historical shape.
|
||||
No user table or row is rewritten in this stage.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Stage 09: Account-Scoped Repository Boundaries
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Added narrow repository ports for alerts, the trade journal, and strategy tracking.
|
||||
- Added SQLite adapters that expose only the persistence operations each service requires.
|
||||
- Required a positive account owner on every private read, write, update, and delete path.
|
||||
- Kept the shared automatic screener-run lookup explicit as the sole zero-owner read in the
|
||||
strategy tracking adapter.
|
||||
- Routed the application container through a repository bundle.
|
||||
- Removed direct alert and trade deletion/update calls from the HTTP-facing dashboard service.
|
||||
- Preserved structural compatibility for isolated tests and gradual extraction from the legacy
|
||||
database facade.
|
||||
|
||||
## Boundary
|
||||
|
||||
Application services depend on repository protocols. The SQLite implementation may later be
|
||||
replaced without changing those services. Repository adapters do not call market providers,
|
||||
and provider adapters do not access user tables.
|
||||
|
||||
## Residual Migration
|
||||
|
||||
The legacy `ReviewDatabase` still contains the SQL behind these adapters and remains the
|
||||
compatibility facade for features not yet extracted. Subsequent feature stages can move SQL
|
||||
behind the same ports one feature at a time after account-isolation tests pass.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Stage 10: Feature Application Service Layout
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Created the governed `backend/features/<feature>` source boundary.
|
||||
- Moved alert behavior into the alerts feature.
|
||||
- Moved trade-journal behavior into the review feature.
|
||||
- Moved strategy-tracking behavior into the screener feature.
|
||||
- Changed the application container to import feature-owned services directly.
|
||||
- Reduced the former top-level service modules to compatibility exports, preserving existing
|
||||
imports without maintaining duplicate implementations.
|
||||
- Added dependency tests that prevent feature services from importing HTTP delivery code or
|
||||
concrete market-provider adapters.
|
||||
|
||||
## Template
|
||||
|
||||
Each migrated feature owns its application behavior and depends on repository or gateway
|
||||
ports. HTTP delivery and background jobs may invoke the service, but neither may become the
|
||||
owner of its rules. The same layout is now available for the remaining market, account,
|
||||
Mentor, Wentian, and screener services.
|
||||
|
||||
## Compatibility
|
||||
|
||||
No route, response shape, persisted data, service method, or top-level import path changes in
|
||||
this stage. Compatibility exports are removed only after all internal and external callers
|
||||
use the feature packages.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Stage 11: HTTP Route, Access, and Error Governance
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Promoted `config/api.config.json` from a transitional inventory to the runtime route and
|
||||
access registry.
|
||||
- Added deterministic exact and regex route resolution with duplicate and regex validation.
|
||||
- Replaced hand-maintained member/admin path sets with the registered access contract.
|
||||
- Rejected unregistered API routes before business dispatch.
|
||||
- Added safe request correlation IDs to JSON responses and `X-Request-ID` headers.
|
||||
- Extended every legacy JSON error with stable `code`, `message`, and `request_id` fields while
|
||||
preserving the existing `error` field used by the browser.
|
||||
- Kept the current request handler and all route response bodies compatible while feature
|
||||
route modules are migrated incrementally.
|
||||
|
||||
## Runtime Authority
|
||||
|
||||
Changing or adding an API now requires one coherent change to the handler and API registry.
|
||||
The generated source inventory test prevents either side from drifting. Backend access remains
|
||||
authoritative; frontend visibility cannot grant a route.
|
||||
|
||||
## Residual Migration
|
||||
|
||||
Individual dispatch branches still live in the compatibility request handler. Feature-owned
|
||||
controllers will move behind the same registry in later stages without changing route identity,
|
||||
authorization, or error serialization.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Stage 12: Governed Background Jobs
|
||||
|
||||
Date: 2026-07-29
|
||||
|
||||
## Result
|
||||
|
||||
- Registered market refresh, automatic screening, and iFinD event enrichment as versioned
|
||||
jobs with schedules, date policy, dependencies, locks, timeouts, retry limits, and output
|
||||
versions.
|
||||
- Added the `job_runs` migration and persistent run ledger.
|
||||
- Centralized worker and scheduler thread creation in `InProcessJobRunner`.
|
||||
- Added process-level lock keys and persistent idempotency keys.
|
||||
- Persisted successful, failed, and retried attempts with elapsed time and normalized error
|
||||
type.
|
||||
- Exposed recent job runs in administrator system status.
|
||||
- Limited retained completed history while preserving running jobs.
|
||||
- Kept existing feature services and the detailed market `sync_runs` audit unchanged.
|
||||
|
||||
## Compatibility
|
||||
|
||||
The runner remains in-process, matching the current single-container deployment. Jobs call the
|
||||
same application methods as HTTP requests and do not depend on request-local account state.
|
||||
The persistent contract permits a later worker process without changing job identities or
|
||||
business calculations.
|
||||
|
||||
## Timeout Boundary
|
||||
|
||||
Timeouts are declared and elapsed time is recorded. Python threads cannot be terminated safely,
|
||||
so hard cancellation remains cooperative until jobs move to a durable worker process. Locks and
|
||||
idempotency prevent concurrent duplicate execution in the current single-process deployment.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Stage 13: Unified LLM Gateway
|
||||
|
||||
## Boundary
|
||||
|
||||
All runtime model calls now enter through `backend/llm/gateway.py`. Feature agents retain
|
||||
their deterministic context assembly, prompt content, and provider response parsing.
|
||||
|
||||
The gateway owns:
|
||||
|
||||
- membership and daily quota enforcement;
|
||||
- primary and fallback model selection;
|
||||
- fallback only before the first streamed delta;
|
||||
- stable user-visible availability and interruption errors;
|
||||
- one logical-call audit record with feature, model role, prompt version, latency, status,
|
||||
normalized error code, and token fields reserved for providers that report usage.
|
||||
|
||||
Administrator connection probes also cross the gateway boundary, but do not consume member
|
||||
quota or create usage records.
|
||||
|
||||
## Compatibility
|
||||
|
||||
- Mentor and review-assistant stream payloads are unchanged.
|
||||
- Heaven readings retain their primary/fallback notice and persistence behavior.
|
||||
- Strategy compilation still falls back to the deterministic local compiler when both model
|
||||
profiles are unavailable, while access and quota failures remain blocking.
|
||||
- Provider credentials and raw provider failures remain outside browser responses.
|
||||
|
||||
## Residual Risk
|
||||
|
||||
The current OpenAI-compatible streaming providers do not consistently return token usage, so
|
||||
the audit schema records zero until transport adapters expose trustworthy token counts. Request
|
||||
cancellation remains bounded by the existing provider socket timeout.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Stage 14: Frontend Request and State Boundaries
|
||||
|
||||
## Request Boundary
|
||||
|
||||
`static/shared/api.js` is now the only application file allowed to call `fetch`. It owns:
|
||||
|
||||
- JSON serialization and response parsing;
|
||||
- CSRF attachment for mutating requests;
|
||||
- expired-session notification;
|
||||
- abort signals;
|
||||
- NDJSON stream decoding and normalized stream errors.
|
||||
|
||||
The mentor and review-assistant streams use the same client as ordinary API requests. Existing
|
||||
function signatures and page interactions remain unchanged.
|
||||
|
||||
## State Boundary
|
||||
|
||||
`static/shared/state.js` stores mutable state in explicit domains: session, market, entity
|
||||
details, review, screener, mentor, and heaven. A compatibility proxy retains the existing flat
|
||||
access syntax while rejecting unregistered fields. New page modules can request their owned
|
||||
domain without depending on another page's data.
|
||||
|
||||
This stage establishes a migration boundary rather than splitting the build-free monolith in a
|
||||
single high-risk edit. Page extraction can now proceed domain by domain with no contract change.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Automated checks require that:
|
||||
|
||||
- only `shared/api.js` contains browser `fetch` calls;
|
||||
- shared state and API scripts load before `app.js`;
|
||||
- application state is created through the shared state boundary.
|
||||
|
||||
## Residual Risk
|
||||
|
||||
`static/app.js` still contains page renderers and event handlers in one file. The state domains
|
||||
make ownership explicit, but those functions should move into page modules only in later,
|
||||
independently verified stages.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Stage 15: Shared Frontend Shell and Page Registry
|
||||
|
||||
## Runtime Page Registry
|
||||
|
||||
`static/pages.config.js` is the build-free runtime representation of
|
||||
`config/pages.config.json`. It registers every primary workspace plus the internal strategy
|
||||
tracking workspace. Automated parity checks prevent titles, feature ownership, access labels,
|
||||
groups, default-page selection, and layout metadata from drifting between the two registries.
|
||||
|
||||
Legacy route aliases are resolved by the registry instead of page business code. The registry
|
||||
describes navigation and presentation metadata only; backend authorization remains
|
||||
authoritative.
|
||||
|
||||
## Shared Shell
|
||||
|
||||
`static/shared/shell.js` now owns:
|
||||
|
||||
- sidebar initialization, persistence, collapse state, and responsive control labels;
|
||||
- primary and mobile navigation binding and active-state synchronization;
|
||||
- workspace mounting, entry animation, URL state, and scroll reset;
|
||||
- header command-menu lifecycle;
|
||||
- market-summary expansion;
|
||||
- status-bar page titles and data dates;
|
||||
- the single-open-dialog lifecycle used by global application dialogs.
|
||||
|
||||
`static/app.js` retains feature-specific enter and leave behavior, such as stopping Wentian
|
||||
animations or loading auction data. It asks the shell to mount a registered page and no longer
|
||||
mutates global page geometry directly.
|
||||
|
||||
## Compatibility
|
||||
|
||||
- Existing DOM IDs, CSS classes, query parameters, page animations, and mobile navigation are
|
||||
unchanged.
|
||||
- `screenerTrackingView` continues to highlight the Intelligent Screener navigation item.
|
||||
- Existing function entry points remain as thin compatibility facades where feature code still
|
||||
calls status, dialog, or command-menu services.
|
||||
|
||||
## Residual Risk
|
||||
|
||||
Feature renderers and feature event binding still share `static/app.js`. Their state ownership
|
||||
is now explicit and their shell dependencies are removed, so later page-module extraction can
|
||||
be performed one feature at a time instead of as a single rewrite.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Stage 16: CSS Tokens and Cascade Governance
|
||||
|
||||
## Scope
|
||||
|
||||
This stage changes CSS ownership, not visual design. Existing selectors, page geometry,
|
||||
responsive behavior, theme behavior, and the isolated Wentian visual layer remain intact.
|
||||
|
||||
## Canonical Token Layer
|
||||
|
||||
`static/shared/tokens.css` is the only owner of global application tokens. It is loaded before
|
||||
all application styles and follows a three-layer contract:
|
||||
|
||||
1. Primitive tokens contain raw palette, dimension, elevation, and motion values.
|
||||
2. Semantic tokens describe interface meaning such as canvas, surface, border, text, action,
|
||||
market direction, and warning states.
|
||||
3. Component tokens define shared card, control, shell, table, chart, and profile contracts.
|
||||
|
||||
Dark mode overrides semantic and compatibility values in the same file. It does not redefine
|
||||
component geometry. The `--wt-*` namespace remains owned by `wentian-v2.css` because Wentian
|
||||
has a deliberately isolated visual language.
|
||||
|
||||
## Compatibility
|
||||
|
||||
The historical `--xb-*`, `--r2-*`, and flat variables such as `--blue`, `--line`, and `--up`
|
||||
remain compatibility aliases. Their effective light and dark values are unchanged. New CSS
|
||||
must use semantic or component tokens; compatibility names exist only so feature selectors can
|
||||
be migrated incrementally without a broad visual rewrite.
|
||||
|
||||
The stylesheet order is fixed as:
|
||||
|
||||
1. shared tokens;
|
||||
2. legacy/base styles;
|
||||
3. renovation and page redesign rules;
|
||||
4. canonical non-Wentian design-system rules;
|
||||
5. runtime theme selectors;
|
||||
6. isolated Wentian rules.
|
||||
|
||||
## Enforcement
|
||||
|
||||
`tests/test_css_governance.py` verifies the load order, the three token layers, single global
|
||||
ownership, compatibility coverage, and Wentian isolation. A new global `:root` token block in
|
||||
any legacy stylesheet fails the regression suite.
|
||||
|
||||
## Residual Migration
|
||||
|
||||
Page styles still contain literal values and historical variable references. Removing those is
|
||||
an incremental page-by-page task because blindly replacing them would risk changing already
|
||||
accepted visuals. Stage 16 establishes the ownership boundary that prevents new drift while
|
||||
leaving safe migration points for later stages.
|
||||
@@ -0,0 +1,53 @@
|
||||
# Stage 17: Frontend Page Modules and Shared Components
|
||||
|
||||
## Scope
|
||||
|
||||
This stage establishes frontend feature ownership without changing page markup, API contracts,
|
||||
permissions, visual design, or user interaction. The build-free deployment model is retained.
|
||||
|
||||
## Page Module Runtime
|
||||
|
||||
`static/pages/runtime.js` owns the page lifecycle registry. Each feature registers its views in
|
||||
`static/pages/<feature>/page.js` and declares named enter and leave actions. The application
|
||||
injects the existing feature functions into that runtime, so page modules do not reach into
|
||||
another feature's state or DOM.
|
||||
|
||||
The lifecycle boundary now owns:
|
||||
|
||||
- page-specific data loading after a successful shell mount;
|
||||
- member-aware entry for Screener, Mentor, and Wentian;
|
||||
- auction timer cleanup when leaving Auction;
|
||||
- canvas, dust, and performance cleanup when leaving Wentian;
|
||||
- the internal Screener Tracking view's ownership relationship.
|
||||
|
||||
`openView` is now a generic coordinator. It validates the route, asks the page runtime to leave
|
||||
the previous page, mounts through the shared shell, and enters the next page. It contains no
|
||||
page-name branch chain.
|
||||
|
||||
## Shared Components
|
||||
|
||||
`static/shared/components.js` is the common rendering boundary for small, stable DOM patterns.
|
||||
The first migrated component is the empty state used across market rotation, themes,
|
||||
Dragon-Tiger, review, Screener, Wentian history, alerts, entity details, and administration.
|
||||
It centralizes escaping and class composition while preserving the exact existing markup.
|
||||
|
||||
Collection rendering and text assignment are exposed for later incremental migrations. They
|
||||
remain dependency-free and use `XiaobaiUI` for safe escaping.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Frontend boundary tests verify that:
|
||||
|
||||
- shared components load after UI primitives and before page code;
|
||||
- the page runtime loads before every feature registration and before `app.js`;
|
||||
- every public and internal workspace belongs to exactly one feature page module;
|
||||
- `openView` contains no feature-specific view comparisons;
|
||||
- shared empty-state rendering is used by multiple feature families;
|
||||
- provider requests still exit only through `shared/api.js`.
|
||||
|
||||
## Compatibility and Residual Risk
|
||||
|
||||
Feature renderers and event handlers still reside in `app.js`; moving them all at once would
|
||||
create a high-risk rewrite across already accepted workflows. The new lifecycle and component
|
||||
boundaries let those functions move feature by feature later without changing navigation or
|
||||
loading behavior. Dedicated mobile composition remains the next governance phase.
|
||||
Reference in New Issue
Block a user