From fdd3e08adef7f6a62d3ef9802fb4353212f19a60 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 21 May 2026 11:02:07 +0800 Subject: [PATCH] Add trader mainline surface audit --- SESSION.md | 4 +- TASKS.md | 4 +- docs/AI_ICT_主线裁剪审计_S1_2026-05-21_v1.md | 216 ++++++++++++ scripts/audit_trader_mainline_surfaces.py | 326 ++++++++++++++++++ tests/unit/test_documentation_consistency.py | 1 + .../test_trader_mainline_surface_audit.py | 67 ++++ 6 files changed, 614 insertions(+), 4 deletions(-) create mode 100644 docs/AI_ICT_主线裁剪审计_S1_2026-05-21_v1.md create mode 100644 scripts/audit_trader_mainline_surfaces.py create mode 100644 tests/unit/test_trader_mainline_surface_audit.py diff --git a/SESSION.md b/SESSION.md index 4c11b78..26cf66b 100644 --- a/SESSION.md +++ b/SESSION.md @@ -831,11 +831,11 @@ Automation execution-layer PXX remains closed and its known scope stays frozen. - Existing-UI i18n polish passed on 2026-05-20 without adding product features: hardened phrase replacement so short words like `no` no longer corrupt larger English words such as `not` / `enough`, then optimized Chinese labels for existing Trader Mode / duty / chart bridge / R2 evidence / runtime workbench surfaces. Remote browser audit on `/trader-mode?audit=20260520e#trader-mode` confirmed no `否t` / `e否ugh` corruption, no old English labels such as `Bridge Status`, `Exact Candle Source`, `Use Today`, `Paper Tickets`, or `Decision Support Ready`, and reduced visible English-remnant audit count from `3396` before the fix to `881` after the fix; the remaining items are mostly symbols/session names/ICT tokens/internal status keys. Validation passed `node --check src/web/i18n.js`, 27 focused static/read-only/operator tests, secret static checks, script static checks, full unit discovery (`789` tests), `git diff --check` with only LF/CRLF warnings, deploy smoke over 11 endpoints, `run_remote_ops_smoke.py`, `run_readonly_live_analysis_smoke.py`, and gstack browser console check with no errors. Evidence screenshot saved at `output/playwright/i18n-existing-ui-polish-zh-1366x768.png`. No change was made to `execution_allowed`, `decision_support_ready`, `real_live_ready`, risk governor, trust bucket, or operator confirmation protocol. - Existing-UI i18n/R2 gate polish follow-up passed on 2026-05-20 without adding product features: expanded the existing Chinese translation map for live-readiness, launch-readiness, shadow/paper validation, runtime states, OKX sandbox preflight labels, and Trader Mode first-screen wording. Remote browser audit on `/trader-mode?audit=20260520k#trader-mode` confirmed `lang=zh-CN`, no `为什么不交易不交易`, no `下一步观察观察候选`, no `操作员反馈 count`, and no old English labels such as `Auto refresh`, `All Trust`, `Incident Journal`, `Maintenance Rollup`, `Plan Context`, `Model Trust`, `Live Readiness`, or `Launch Readiness`; the remaining English-token count in the active Trader Mode view is `13`, mostly `English`, `ai-ict`, timezone/profile names, strategy codes, and feedback enum values. Trader Mode R2 evidence display now follows `shadow_paper_validation_summary.failed_criteria`, so the evidence window no longer says operator feedback is complete while the canonical shadow validation gate still requires more samples. Current live evidence is still collecting: `operator_feedback_count=3/5`, `paper_ticket_count=0`, `paper_execution_count=0`, and `weekly_review_entry_count=0`. Validation passed focused i18n/operator/static tests, secret static checks, script static checks, full unit discovery (`790` tests), deploy smoke over 11 endpoints, `run_remote_ops_smoke.py`, `run_readonly_live_analysis_smoke.py`, and gstack browser console check with no errors. Evidence screenshot saved at `output/playwright/i18n-existing-ui-polish-zh-followup-1366x768.png`. No change was made to `execution_allowed`, `decision_support_ready`, `real_live_ready`, risk governor, trust bucket, or operator confirmation protocol. - Session Focus Chinese dynamic-label follow-up passed on 2026-05-20 without adding product features: fixed the existing i18n layer for dynamic session-focus cards so session names and lower-case backend labels now render in Chinese (`ASIA`, `LONDON`, `NY_AM`, `NY_LUNCH`, `NY_PM`, `status`, `window`, `candidates`, `executable`, `open executions`, `alerts`, `recommendation`, `action hint`, `incomplete`, `closed`, `observe`, `avoid`, `review_alerts`, `avoid_new_risk`). Remote browser audit on `/trader-mode?i18n=20260520-session#session-focus` confirmed `lang=zh-CN`, `active=session-focus`, no English leftovers from the reported list, and visible Chinese replacements such as `亚洲盘`, `纽约下午盘`, `状态`, `时间窗口`, `候选数`, `可执行数`, `未平仓执行`, `提醒数`, `建议`, `动作提示`, `观察 @ 无`, `回避 @ 30.0`, `回避新增风险`, and `复核提醒`; console errors were empty. Validation passed `node --check src/web/i18n.js`, focused static/operator/read-only tests, secret static checks, script static checks, full unit discovery (`790` tests), deploy smoke over 11 endpoints, `run_remote_ops_smoke.py`, and `run_readonly_live_analysis_smoke.py`. Evidence screenshot saved at `output/playwright/i18n-session-focus-zh-1366x768.png`. No change was made to `execution_allowed`, `decision_support_ready`, `real_live_ready`, risk governor, trust bucket, or operator confirmation protocol. -- Current assisted-analysis next gate: keep R1/R2 focused on `decision_support_ready` trust/sample/quality evidence and shadow/paper/operator feedback collection. OKX trade-permission sandbox smoke is deferred until the project explicitly re-enters execution adapter or real-trading preparation. +- Current assisted-analysis next gate: S1 mainline deletion readiness audit is complete via `docs/AI_ICT_主线裁剪审计_S1_2026-05-21_v1.md` and `scripts/audit_trader_mainline_surfaces.py`. The top navigation is already reduced to Trader Cockpit / Strategy Backtest / Trading Plan / Journal / System Ops; remaining non-mainline sections are classified under System Ops, Research Only, or Deprecated / Deferred Execution, with no physical deletion allowed yet. Keep R1/R2 focused on `decision_support_ready` trust/sample/quality evidence and shadow/paper/operator feedback collection. OKX trade-permission sandbox smoke is deferred until the project explicitly re-enters execution adapter or real-trading preparation. ## Exact Next Step -Use `docs/AI_ICT_实盘辅助分析决策助手ICT主线开发方案_2026-05-21_v1.md` as the next active development anchor. Continue with S1 mainline deletion readiness audit: scan all non-mainline trader-facing routes, sidebar sections, API endpoints, frontend panels, and tests; classify each as `Mainline`, `System Ops`, `Research Only`, `Deprecated`, or `Delete Candidate`; do not physically delete shared or diagnostic code until dependency, API usage, test coverage, rollback, and remote smoke impact are clear. Every follow-up slice must map to `docs/ICT交易全书_出版终校版.md` and the mandatory chain `Range / Draw / Location -> Liquidity / Sweep / Inducement / Path -> Displacement / MSS / FVG / Retest / Session -> backtest evidence -> pre-market plan -> intraday unique focus or no-trade reason -> journal / study-notes feedback`. Do not restart external-context/archive/digest long-tail work, do not create order-submission features, and do not fabricate R2 paper samples. Keep `execution_allowed`, `decision_support_ready`, `real_live_ready`, risk governor, trust bucket, and operator confirmation protocol unchanged. +Use `docs/AI_ICT_实盘辅助分析决策助手ICT主线开发方案_2026-05-21_v1.md` and `docs/AI_ICT_主线裁剪审计_S1_2026-05-21_v1.md` as the active anchors. Continue with S2 ICT canonical taxonomy / rulebook registry: define the book-backed concepts required by every Trader Cockpit focus, Strategy Backtest result, Trading Plan item, and Journal note, then add focused validation that prevents trader-facing output from using unclassified internal reason buckets without the mandatory `Range / Draw / Location -> Liquidity / Sweep / Inducement / Path -> Displacement / MSS / FVG / Retest / Session -> backtest evidence -> pre-market plan -> intraday unique focus or no-trade reason -> journal / study-notes feedback` mapping. Do not restart external-context/archive/digest long-tail work, do not create order-submission features, do not physically delete S1 deprecated surfaces yet, and do not fabricate R2 paper samples. Keep `execution_allowed`, `decision_support_ready`, `real_live_ready`, risk governor, trust bucket, and operator confirmation protocol unchanged. ## Validation Commands Used Recently diff --git a/TASKS.md b/TASKS.md index 809b20c..d91c9fb 100644 --- a/TASKS.md +++ b/TASKS.md @@ -933,8 +933,8 @@ - [x] Trader mainline Step 3 Strategy Backtest productization: added read-only `/trading/backtests`, Strategy Backtest workbench controls, latest-run summary, quality gate explanation, R metrics, equity curve, top sessions, failure reasons, recent run list, Chinese labels for backtest gate reasons, and resilient candle/backtest failure fallback; full unit (`791`), deploy smoke, remote ops smoke, readonly live smoke, remote backtest endpoint smoke, and browser audit passed - [x] Trader mainline Step 4 cockpit recompression implemented/deployed: under the hard ICT book mainline gate, Trader Cockpit first screen now reduces to `Now / Reason / Wait / Risk / Historical Edge / Boundary` and uses latest Strategy Backtest evidence without changing any execution/readiness gate; deploy smoke, remote ops smoke, remote backtest field smoke, browser QA, and the recovered read-only launch smoke passed with `ready_for_readonly_live_analysis=true`, `must_not_trade=true`, `trade_permission_required=false`, and no submit/cancel/live execution calls - [x] Created next-stage ICT mainline development anchor: `docs/AI_ICT_实盘辅助分析决策助手ICT主线开发方案_2026-05-21_v1.md`, binding all future work to `docs/ICT交易全书_出版终校版.md`, read-only assisted analysis, Strategy Backtest, Trading Plan, Trader Cockpit, Journal, and System Ops separation -- [ ] Trader mainline Step 5 deletion readiness audit: scan non-mainline trader-facing surfaces for dependency/API/test coverage before any code-level deletion; keep diagnostics under `System Ops` unless proven removable -- [ ] S1 mainline deletion readiness audit under the new plan: classify routes/sidebar/API/frontend panels/tests as `Mainline`, `System Ops`, `Research Only`, `Deprecated`, or `Delete Candidate`, then hide/migrate before considering physical deletion +- [x] Trader mainline Step 5 deletion readiness audit: added `scripts/audit_trader_mainline_surfaces.py`, classified 5 top-nav targets, 19 workspace sections, 80 API routes, 43 frontend API references, and 108 test endpoint references before any deletion; diagnostics stay under `System Ops` +- [x] S1 mainline deletion readiness audit under the new plan: created `docs/AI_ICT_主线裁剪审计_S1_2026-05-21_v1.md`; current top navigation is compliant, Research Only and Deprecated/Deferred Execution surfaces are documented, and physical deletion remains blocked until dependency/API/test/remote-smoke impact is proven safe - [ ] S2 ICT canonical taxonomy/rulebook registry: require every Trader Cockpit focus, Strategy Backtest result, Trading Plan item, and Journal note to map to the book chain - [ ] S3/S4 follow-up: make Trading Plan a hard upstream gate for `Review Now`, then deepen Strategy Backtest so quality-gate failure blocks weak setups from live focus - [ ] R2 paper-ticket sample gate is waiting on a real executable candidate; do not fabricate paper tickets from blocked candidates diff --git a/docs/AI_ICT_主线裁剪审计_S1_2026-05-21_v1.md b/docs/AI_ICT_主线裁剪审计_S1_2026-05-21_v1.md new file mode 100644 index 0000000..e721130 --- /dev/null +++ b/docs/AI_ICT_主线裁剪审计_S1_2026-05-21_v1.md @@ -0,0 +1,216 @@ +# AI_ICT 主线裁剪审计 S1 2026-05-21 v1 + +## 0. 审计边界 + +本审计执行 `docs/AI_ICT_实盘辅助分析决策助手ICT主线开发方案_2026-05-21_v1.md` 的 S1。 + +目标不是立刻删除代码,而是先把交易员主线、System Ops、研究保留面、延期/废弃执行面分清楚,避免继续把复杂功能塞回盘中主界面。 + +硬边界保持不变: + +```text +execution_allowed=false +decision_support_ready=false +real_live_ready=false +must_not_trade=true +trade_permission_required=false +submit_cancel_or_live_execution_called=false +``` + +## 1. 审计方法 + +新增可重复扫描脚本: + +```bash +python scripts/audit_trader_mainline_surfaces.py --output-json +``` + +扫描范围: + +- `src/web/index.html` 的顶层导航、workspace sections、System Ops links。 +- `src/web/app.js` 与 `src/web/trader_mode.js` 的 `fetchJson` / `postJson` 前端 API 引用。 +- `src/api/server.py` 的显式 API routes。 +- `tests/unit/test_*.py` 中的 endpoint 测试引用。 + +本轮扫描结果: + +```text +top nav targets: 5 +workspace sections: 19 +system ops targets: 14 +api routes: 80 +frontend api references: 43 +test endpoint references: 108 +``` + +分类计数: + +```text +workspace sections: +- Mainline: 4 +- System Ops: 10 +- Research Only: 3 +- Deprecated: 2 + +api routes: +- Mainline: 16 +- System Ops: 26 +- Research Only: 33 +- Deprecated: 5 + +frontend api references: +- Mainline: 12 +- System Ops: 20 +- Research Only: 9 +- Deprecated: 2 +``` + +## 2. 顶层导航结论 + +当前顶层导航已经符合新方案: + +| 导航入口 | 分类 | 结论 | +| --- | --- | --- | +| `trader-mode` / Trader Cockpit | Mainline | 保留,盘中唯一首屏 | +| `strategy-backtest` / Strategy Backtest | Mainline | 保留,验证 ICT setup 是否有历史优势 | +| `trading-plan` / Trading Plan | Mainline | 保留,后续作为 `Review Now` 的上游硬门 | +| `journal` / Journal | Mainline | 保留,真实观察与盘后复盘入口 | +| `system-ops` / System Ops | System Ops | 保留,承接复杂诊断与旧功能 | + +结论:顶层菜单不需要继续扩展;后续新增功能默认不得进入顶层导航。 + +## 3. Workspace Section 分类 + +| Section | 分类 | S1 决策 | +| --- | --- | --- | +| `trader-mode` | Mainline | 保留,继续压缩为 ICT fast read | +| `strategy-backtest` | Mainline | 保留,后续深化 book-rule quality gate | +| `trading-plan` | Mainline | 保留,后续重构为书本顺序计划表 | +| `journal` | Mainline | 保留,后续对齐 Study Notes / Journal 闭环 | +| `system-ops` | System Ops | 保留,复杂能力的唯一入口 | +| `overview` | System Ops | 保留在 System Ops,不能回到主线 | +| `trading-status` | System Ops | 保留为诊断,不作为交易指令 | +| `runtime-supervisor` | System Ops | 保留为运行时诊断 | +| `decision-packet` | System Ops | 保留为审计材料,不作为盘中主屏 | +| `readiness` | System Ops | 保留,显示 readiness/trust 边界 | +| `session-focus` | System Ops | 保留为 ICT session 辅助诊断,后续可被 Trading Plan 引用 | +| `intraday-candidates` | System Ops | 保留为候选调试,不直接给交易员主屏 | +| `alerts` | System Ops | 保留为运营/告警质量诊断 | +| `candles` | System Ops | 保留为只读行情诊断 | +| `decision-assistant` | Research Only | 冻结主线扩张,仅作为研究/内部审计面 | +| `visual-reviews` | Research Only | 冻结主线扩张,不能替代书本链路 | +| `operator-console` | Research Only | 保留为内部研究/操作面,不进入主线 | +| `execution-tickets` | Deprecated | 当前不做交易,保留代码但不得突出展示为交易员主功能 | +| `position-management` | Deprecated | 当前不做实盘持仓管理,保留代码但不得进入主线 | + +本轮不做物理删除。原因:`Deprecated` 不等于“确认无依赖”,仍需后续检查动态路由、测试覆盖和远端 smoke 影响。 + +## 4. API 分类结论 + +### 4.1 Mainline API + +这些 API 直接服务 Trader Cockpit、Strategy Backtest、Trading Plan、Journal 或只读主线: + +- `/health` +- `/candles` +- `/trading/backtests` +- `/trading/plans` +- `/trading/plans/latest` +- `/trading/plans/generate` +- `/trading/review-packet` +- `/trading/operator-notes` +- `/trading/shadow-paper/observe` +- `/trading/shadow-paper-validation` +- `/trading/journal` +- `/trading/journal/summary` +- `/trading/journal/generate` +- `/trading/research/weekly-review` +- `/trading/launch-readiness` + +结论:保留,后续必须全部映射到《ICT交易全书》的主线字段。 + +### 4.2 System Ops API + +这些 API 保留用于运行时、数据、候选、告警和只读可用性诊断: + +- `/trading/runtime-status` +- `/trading/runtime-watchdog` +- `/trading/runtime-incidents` +- `/trading/runtime/run-once` +- `/trading/maintenance-summary` +- `/trading/unattended-harness` +- `/trading/live-readiness` +- `/trading/broker-state` +- `/trading/market-state` +- `/trading/trust-score` +- `/trading/session-view` +- `/trading/candidates` +- `/trading/decision-packet` +- `/trading/alerts/*` +- `/trading/watch/run` + +结论:保留在 System Ops,不得作为交易员主线功能继续膨胀。 + +### 4.3 Research Only API + +这些 API 来自历史 archive、workflow、external context、visual watch、research council、training 等长尾能力: + +- `/trading/archive-baselines` +- `/trading/assistant-focus` +- `/trading/external-context-surface-history` +- `/trading/research-council/*` +- `/trading/visual-watch/*` +- `/trading/workflow-archive/*` +- `/training/*` + +结论:冻结主线扩张。只有当某个能力能明确服务 `Range / Draw / Liquidity / MSS / FVG / Backtest / Plan / Journal` 链路时,才允许被重新纳入主线。 + +### 4.4 Deprecated / Deferred Execution API + +这些 API 带有执行、票据、持仓、OKX sandbox 或 reconciliation 语义: + +- `/live-operation-audits` +- `/trading/okx-sandbox-preflight` +- `/trading/reconcile` +- `/trading/executions` +- `/trading/tickets` +- `/trading/candidates/{id}/build-ticket` + +结论: + +- 当前只做实盘辅助分析,不做交易。 +- 这些 API 不得进入 Trader Cockpit / Strategy Backtest / Trading Plan / Journal 主线。 +- 代码暂时不删,保留在 System Ops / deferred execution 区域,等待用户明确进入执行阶段后再处理。 + +## 5. 删除准备结论 + +本轮没有发现可以立即物理删除且零风险的文件或 API。 + +原因: + +- 多数旧能力仍有测试覆盖和前端引用。 +- `execution-tickets` / `position-management` 虽然不属于当前阶段,但仍和 R2 paper / future execution evidence 历史代码交织。 +- `archive` / `workflow` / `external context` 虽偏离主线,但大量 API 与 operator workspace 测试仍覆盖,直接删除风险高。 +- 当前最重要的是防止它们回到交易员主界面,而不是为了“干净”破坏已有远端 smoke。 + +## 6. 后续裁剪规则 + +后续任何裁剪必须按以下顺序: + +1. 先确认是否服务《ICT交易全书》主线。 +2. 不服务主线但服务运行诊断的,迁到 System Ops。 +3. 不服务主线且只是研究长尾的,标记 Research Only 并冻结扩张。 +4. 带执行/下单/票据/持仓语义的,在当前阶段标记 Deprecated / Deferred Execution。 +5. 只有同时满足“无主线价值、无 System Ops 价值、无测试依赖、无前端引用、无远端 smoke 依赖、可回滚”时,才进入 Delete Candidate。 + +## 7. 下一步 + +S1 审计已经完成。下一步进入 S2: + +```text +ICT canonical taxonomy / rulebook registry +``` + +S2 目标是建立统一书本模型字典,让 Trader Cockpit focus、Strategy Backtest result、Trading Plan item、Journal note 都必须映射到《ICT交易全书》的固定链路。 + +下一步不应继续做 external context、archive、workflow、digest 长尾,也不应做 OKX trade permission / submit / cancel / fills。 diff --git a/scripts/audit_trader_mainline_surfaces.py b/scripts/audit_trader_mainline_surfaces.py new file mode 100644 index 0000000..f1cabac --- /dev/null +++ b/scripts/audit_trader_mainline_surfaces.py @@ -0,0 +1,326 @@ +from __future__ import annotations + +import argparse +from collections import Counter, defaultdict +import json +from pathlib import Path +import re + + +ROOT = Path(__file__).resolve().parents[1] +INDEX_HTML = ROOT / "src" / "web" / "index.html" +APP_JS = ROOT / "src" / "web" / "app.js" +TRADER_MODE_JS = ROOT / "src" / "web" / "trader_mode.js" +API_SERVER = ROOT / "src" / "api" / "server.py" +TESTS_DIR = ROOT / "tests" / "unit" + +MAINLINE_SECTION_IDS = { + "trader-mode", + "strategy-backtest", + "trading-plan", + "journal", +} + +SYSTEM_OPS_SECTION_IDS = { + "system-ops", + "overview", + "trading-status", + "runtime-supervisor", + "decision-packet", + "readiness", + "session-focus", + "intraday-candidates", + "alerts", + "candles", +} + +RESEARCH_ONLY_SECTION_IDS = { + "decision-assistant", + "operator-console", + "visual-reviews", +} + +DEPRECATED_SECTION_IDS = { + "execution-tickets", + "position-management", +} + +MAINLINE_ENDPOINT_PREFIXES = ( + "/health", + "/candles", + "/trading/backtests", + "/trading/plans", + "/trading/review-packet", + "/trading/operator-notes", + "/trading/shadow-paper", + "/trading/shadow-paper-validation", + "/trading/journal", + "/trading/research/weekly-review", + "/trading/launch-readiness", +) + +SYSTEM_OPS_ENDPOINT_PREFIXES = ( + "/trading/broker-state", + "/trading/market-state", + "/trading/runtime", + "/trading/maintenance-summary", + "/trading/unattended-harness", + "/trading/trust-score", + "/trading/session-view", + "/trading/candidates", + "/trading/decision-packet", + "/trading/alerts", + "/trading/live-readiness", +) + +RESEARCH_ONLY_ENDPOINT_PREFIXES = ( + "/trading/archive-baselines", + "/trading/assistant-focus", + "/trading/external-context", + "/trading/research-council", + "/trading/visual-watch", + "/trading/workflow-archive", + "/training", +) + +DEPRECATED_ENDPOINT_PREFIXES = ( + "/live-operation-audits", + "/trading/okx-sandbox-preflight", + "/trading/reconcile", + "/trading/executions", + "/trading/tickets", +) + +ROUTE_RE = re.compile(r'if method == "([A-Z]+)" and path == "([^"]+)"') +SECTION_RE = re.compile( + r'data-workbench-section="([^"]+)"[^>]*data-workbench-label="([^"]+)"', + re.MULTILINE, +) +NAV_RE = re.compile(r'data-workbench-target="([^"]+)"[^>]*>([^<]+)') +SYSTEM_OPS_RE = re.compile(r'data-system-ops-target="([^"]+)"[^>]*>([^<]+)') +FRONTEND_ENDPOINT_RE = re.compile( + r"(?:fetchJson|postJson)\(\s*(?:`([^`]+)`|'([^']+)'|\"([^\"]+)\")" +) +TEST_ENDPOINT_RE = re.compile( + r"['\"](/(?:health|candles|trader-mode|static|live-operation-audits|training|trading)[^'\"]*)['\"]" +) + + +def normalize_endpoint(raw: str) -> str: + endpoint = raw.split("?", 1)[0].strip() + endpoint = re.sub(r"\$\{[^}]+\}", "{expr}", endpoint) + endpoint = re.sub(r"/\d+(?=/|$)", "/{id}", endpoint) + endpoint = endpoint.rstrip("/") + return endpoint or raw + + +def classify_section(section_id: str) -> str: + if section_id in MAINLINE_SECTION_IDS: + return "Mainline" + if section_id in SYSTEM_OPS_SECTION_IDS: + return "System Ops" + if section_id in RESEARCH_ONLY_SECTION_IDS: + return "Research Only" + if section_id in DEPRECATED_SECTION_IDS: + return "Deprecated" + return "Unclassified" + + +def classify_endpoint(endpoint: str) -> str: + normalized = normalize_endpoint(endpoint) + if normalized.startswith("/static") or normalized in {"/trader-mode", "/trader-mode.html"}: + return "Mainline" + if "/build-ticket" in normalized: + return "Deprecated" + if normalized.startswith(DEPRECATED_ENDPOINT_PREFIXES): + return "Deprecated" + if normalized.startswith(RESEARCH_ONLY_ENDPOINT_PREFIXES): + return "Research Only" + if normalized.startswith(MAINLINE_ENDPOINT_PREFIXES): + return "Mainline" + if normalized.startswith(SYSTEM_OPS_ENDPOINT_PREFIXES): + return "System Ops" + if normalized == "/trading/watch/run": + return "System Ops" + return "Unclassified" + + +def read_text(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def collect_workspace_sections(index_html: Path = INDEX_HTML) -> list[dict]: + text = read_text(index_html) + return [ + { + "id": section_id, + "label": label, + "category": classify_section(section_id), + } + for section_id, label in SECTION_RE.findall(text) + ] + + +def collect_nav_targets(index_html: Path = INDEX_HTML) -> list[dict]: + text = read_text(index_html) + return [ + { + "id": target, + "label": label.strip(), + "category": classify_section(target), + } + for target, label in NAV_RE.findall(text) + ] + + +def collect_system_ops_targets(index_html: Path = INDEX_HTML) -> list[dict]: + text = read_text(index_html) + return [ + { + "id": target, + "label": label.strip(), + "category": classify_section(target), + } + for target, label in SYSTEM_OPS_RE.findall(text) + ] + + +def collect_api_routes(api_server: Path = API_SERVER) -> list[dict]: + text = read_text(api_server) + routes = [] + for method, endpoint in ROUTE_RE.findall(text): + normalized = normalize_endpoint(endpoint) + routes.append( + { + "method": method, + "endpoint": normalized, + "category": classify_endpoint(normalized), + "source": "src/api/server.py", + } + ) + return sorted(routes, key=lambda item: (item["endpoint"], item["method"])) + + +def collect_frontend_api_references(paths=None) -> list[dict]: + paths = [APP_JS, TRADER_MODE_JS] if paths is None else paths + references = {} + for path in paths: + text = read_text(path) + for match in FRONTEND_ENDPOINT_RE.findall(text): + raw = next(part for part in match if part) + endpoint = normalize_endpoint(raw) + references.setdefault(endpoint, set()).add(display_path(path)) + return [ + { + "endpoint": endpoint, + "category": classify_endpoint(endpoint), + "sources": sorted(sources), + } + for endpoint, sources in sorted(references.items()) + ] + + +def collect_test_endpoint_references(tests_dir: Path = TESTS_DIR) -> list[dict]: + references = defaultdict(set) + for path in sorted(tests_dir.glob("test_*.py")): + text = read_text(path) + for raw in TEST_ENDPOINT_RE.findall(text): + endpoint = normalize_endpoint(raw) + references[endpoint].add(display_path(path)) + return [ + { + "endpoint": endpoint, + "category": classify_endpoint(endpoint), + "sources": sorted(sources), + } + for endpoint, sources in sorted(references.items()) + ] + + +def build_audit() -> dict: + sections = collect_workspace_sections() + nav_targets = collect_nav_targets() + system_ops_targets = collect_system_ops_targets() + api_routes = collect_api_routes() + frontend_references = collect_frontend_api_references() + test_references = collect_test_endpoint_references() + + endpoint_categories = Counter(item["category"] for item in api_routes) + frontend_categories = Counter(item["category"] for item in frontend_references) + section_categories = Counter(item["category"] for item in sections) + + return { + "policy": { + "mainline_chain": ( + "Range / Draw / Location -> Liquidity / Sweep / Inducement / Path " + "-> Displacement / MSS / FVG / Retest / Session -> backtest evidence " + "-> pre-market plan -> intraday focus/no-trade -> journal feedback" + ), + "execution_boundary": "read_only_analysis_no_order_submission", + "physical_delete_allowed": False, + }, + "nav_targets": nav_targets, + "workspace_sections": sections, + "system_ops_targets": system_ops_targets, + "api_routes": api_routes, + "frontend_api_references": frontend_references, + "test_endpoint_references": test_references, + "summary": { + "nav_count": len(nav_targets), + "workspace_section_count": len(sections), + "system_ops_target_count": len(system_ops_targets), + "api_route_count": len(api_routes), + "frontend_api_reference_count": len(frontend_references), + "test_endpoint_reference_count": len(test_references), + "section_categories": dict(sorted(section_categories.items())), + "api_route_categories": dict(sorted(endpoint_categories.items())), + "frontend_reference_categories": dict(sorted(frontend_categories.items())), + }, + } + + +def format_markdown(audit: dict) -> str: + lines = [ + "# Trader Mainline Surface Audit", + "", + f"- Execution boundary: `{audit['policy']['execution_boundary']}`", + f"- Physical delete allowed now: `{str(audit['policy']['physical_delete_allowed']).lower()}`", + f"- Mainline chain: {audit['policy']['mainline_chain']}", + "", + "## Navigation", + "", + "| Target | Label | Category |", + "| --- | --- | --- |", + ] + for item in audit["nav_targets"]: + lines.append(f"| `{item['id']}` | {item['label']} | {item['category']} |") + lines.extend(["", "## Workspace Sections", "", "| Section | Label | Category |", "| --- | --- | --- |"]) + for item in audit["workspace_sections"]: + lines.append(f"| `{item['id']}` | {item['label']} | {item['category']} |") + lines.extend(["", "## API Route Category Counts", "", "```json"]) + lines.append(json.dumps(audit["summary"]["api_route_categories"], ensure_ascii=False, sort_keys=True, indent=2)) + lines.extend(["```", ""]) + return "\n".join(lines) + + +def display_path(path: Path) -> str: + try: + return str(path.relative_to(ROOT)).replace("\\", "/") + except ValueError: + return str(path) + + +def main(argv=None) -> int: + parser = argparse.ArgumentParser(description="Audit trader-facing surfaces against the ICT mainline plan.") + parser.add_argument("--output-json", action="store_true", help="Print JSON instead of Markdown.") + args = parser.parse_args(argv) + audit = build_audit() + if args.output_json: + print(json.dumps(audit, ensure_ascii=False, sort_keys=True, indent=2)) + else: + print(format_markdown(audit)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/unit/test_documentation_consistency.py b/tests/unit/test_documentation_consistency.py index 279ab3f..81b69f1 100644 --- a/tests/unit/test_documentation_consistency.py +++ b/tests/unit/test_documentation_consistency.py @@ -20,6 +20,7 @@ NONEXISTENT_VISUAL_PLAN_NAME = "AI_ICT_" + "视觉盯盘与主动告警增强方 ACTIVE_TOP_LEVEL_DOCS = { "AI_ICT_硬性主线约束_必须遵循ICT交易全书_2026-05-21_v1.md", "AI_ICT_实盘辅助分析决策助手ICT主线开发方案_2026-05-21_v1.md", + "AI_ICT_主线裁剪审计_S1_2026-05-21_v1.md", "AI_ICT_主线纠偏与功能裁剪方案_2026-05-20_v1.md", "AI_ICT_实盘辅助分析决策助手上线必做清单_2026-05-18_v1.md", "AI_ICT_实盘辅助分析决策助手使用说明_2026-05-18_v1.md", diff --git a/tests/unit/test_trader_mainline_surface_audit.py b/tests/unit/test_trader_mainline_surface_audit.py new file mode 100644 index 0000000..05cf540 --- /dev/null +++ b/tests/unit/test_trader_mainline_surface_audit.py @@ -0,0 +1,67 @@ +import unittest + +from scripts import audit_trader_mainline_surfaces as audit + + +class TraderMainlineSurfaceAuditTests(unittest.TestCase): + def test_main_navigation_is_reduced_to_five_trader_entries(self) -> None: + nav_targets = audit.collect_nav_targets() + self.assertEqual( + [item["id"] for item in nav_targets], + ["trader-mode", "strategy-backtest", "trading-plan", "journal", "system-ops"], + ) + self.assertEqual( + [item["category"] for item in nav_targets], + ["Mainline", "Mainline", "Mainline", "Mainline", "System Ops"], + ) + + def test_workspace_sections_are_classified_before_deletion(self) -> None: + sections = audit.collect_workspace_sections() + categories = {item["id"]: item["category"] for item in sections} + self.assertEqual(categories["trader-mode"], "Mainline") + self.assertEqual(categories["strategy-backtest"], "Mainline") + self.assertEqual(categories["trading-plan"], "Mainline") + self.assertEqual(categories["journal"], "Mainline") + self.assertEqual(categories["system-ops"], "System Ops") + self.assertEqual(categories["decision-assistant"], "Research Only") + self.assertEqual(categories["visual-reviews"], "Research Only") + self.assertEqual(categories["execution-tickets"], "Deprecated") + self.assertEqual(categories["position-management"], "Deprecated") + self.assertNotIn("Unclassified", categories.values()) + + def test_system_ops_links_cover_non_mainline_sections(self) -> None: + sections = {item["id"] for item in audit.collect_workspace_sections()} + system_ops_targets = audit.collect_system_ops_targets() + for item in system_ops_targets: + self.assertIn(item["id"], sections) + self.assertNotEqual(item["category"], "Mainline") + + def test_endpoint_classification_keeps_execution_surfaces_out_of_mainline(self) -> None: + self.assertEqual(audit.classify_endpoint("/trading/backtests"), "Mainline") + self.assertEqual(audit.classify_endpoint("/trading/plans/latest"), "Mainline") + self.assertEqual(audit.classify_endpoint("/trading/journal"), "Mainline") + self.assertEqual(audit.classify_endpoint("/trading/workflow-archive"), "Research Only") + self.assertEqual(audit.classify_endpoint("/trading/external-context-surface-history"), "Research Only") + self.assertEqual(audit.classify_endpoint("/trading/tickets"), "Deprecated") + self.assertEqual(audit.classify_endpoint("/trading/executions"), "Deprecated") + self.assertEqual(audit.classify_endpoint("/trading/okx-sandbox-preflight"), "Deprecated") + self.assertEqual(audit.classify_endpoint("/trading/candidates/4/build-ticket"), "Deprecated") + self.assertEqual(audit.classify_endpoint("/trading/watch/run"), "System Ops") + + def test_frontend_api_references_are_classified(self) -> None: + references = audit.collect_frontend_api_references() + categories = {item["endpoint"]: item["category"] for item in references} + self.assertEqual(categories["/trading/backtests"], "Mainline") + self.assertEqual(categories["/trading/workflow-archive"], "Research Only") + self.assertEqual(categories["/trading/executions"], "Deprecated") + self.assertNotIn("Unclassified", categories.values()) + + def test_audit_summary_preserves_read_only_boundary(self) -> None: + result = audit.build_audit() + self.assertFalse(result["policy"]["physical_delete_allowed"]) + self.assertEqual(result["policy"]["execution_boundary"], "read_only_analysis_no_order_submission") + self.assertEqual(result["summary"]["nav_count"], 5) + + +if __name__ == "__main__": + unittest.main()