Add trader mainline surface audit

This commit is contained in:
Codex
2026-05-21 11:02:07 +08:00
parent db1edf5c66
commit fdd3e08ade
6 changed files with 614 additions and 4 deletions
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
@@ -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。
+326
View File
@@ -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="([^"]+)"[^>]*>([^<]+)</button>')
SYSTEM_OPS_RE = re.compile(r'data-system-ops-target="([^"]+)"[^>]*>([^<]+)</button>')
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())
@@ -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",
@@ -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()