diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed9ec20..b306c98 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,9 +4,30 @@
> 桌面 / Android 四平台发布资产见 [GitHub Releases](https://github.com/ROM4n2/DeLector/releases);
> 开发决策细节见 `docs/plans/archive/` 与 Obsidian Vault `08-Projects/DeLector/01-ADR/`。
-**最新版本:v5.18.0(2026-10-19)**
+**最新版本:v5.19.0(2026-10-22)**
-### 🏗️ 架构与前端单体彻底解耦 + 交互式智能体闭环 + 六道自动化机械闸(PR #131~#161)
+### 🖥️ 桌面单实例锁 + 端口顺延 + 前端继续解耦(PR #172 · #164~#166)
+
+自 v5.18.0(`4646a67`,2026-10-19)起本版收敛两个主题:**「同一台机器只能有一个 DeLector」成为 OS 级事实**,以及前端单体继续分层。全量测试 2,438 → **2,584 passed + 2 skipped(净增 146)**。
+
+**① 桌面单实例(判据从「端口」换成「锁」)**
+- **OS 文件锁**:`%LOCALAPPDATA%\DeLector\instance.lock`(POSIX `~/.local/share/DeLector/instance.lock`),同目录 `instance.json` 记 `pid / 版本 / 端口 / 数据目录 / 抢锁时刻`。此前判据是「8000 上有没有东西」——那只说明端口被占,不说明占用者是谁。
+- **锁与元数据分两个文件**:POSIX `flock` 锁的是 **inode**;若对同一文件 `os.replace`,旧 fd 锁着已 unlink 的旧 inode,下一个进程能抢到锁 ⇒ 锁静默失效。故 `instance.lock` 只做锁目标、永不替换。
+- **唯一判定点** `start.claim_instance`:`start.main()`(脚本 / `--server-only`)与桌面壳共用,归因优先级 `unknown > version_mismatch > data_dir_mismatch > same`,杜绝「壳说没有、服务说有两个」。
+- **第二腿**:锁降级时(只读目录 / 无锁原语 / 文件打不开)先对被占候选端口做 `/api/health` 身份探测 —— 否则会为同一个库起第二个服务。真锁在手时一律不探测。
+- 桌面壳退出码 **3** 扩展到六种冲突情形;文案只含人话(版本 / pid / 端口 / 数据目录 / 日志路径),traceback 只进 `launch.log`。
+
+**② 端口顺延与真实端口**
+- 候选 `8000, 8002–8010`(**8001 是 Go supervisor 保留端口**,显式指定也拒绝);自动模式顺延并留一条含新旧端口的日志,显式 `--port` **不顺延**(已对外承诺的地址不能偷偷换)。
+- `GET /api/wb/lan-info` 改报 `app.state.server_port`(`build_server` 写入的真实端口)。此前写死 8000 —— 顺延后手机会按错端口配对、超时后提示失败,而主机日志一切正常。
+
+**③ 前端单体继续解耦(#164~#166)**
+- `reader.js` 抽出 `rd-syntax.js`(纯语法层)与 `rd-api.js`(L2 持久化层,14 个端点函数);`cards.js` 的 15 个模块级可变状态收进纯状态容器 `cd-state.js`,视图层顶层 `let` 15 → 0。导出面全程冻结(`reader.js` 40 个具名、`cards.js` 36 个 + 1 条 barrel)。
+- 本批真正的产出是挖出两条**与重构无关的既有静默失效**:`ia_dom_mount_probe.mjs` 有 4 条守卫一直恒真(切片锚点只到函数名);探针静态层已红时仍跑 `node:vm`,空源撞出的 `ReferenceError` 排在真实问题后面。纪律已写进 `docs/agents/architecture.md`「前端模块分层契约」。
+
+**回滚**:单实例锁可用 `DELECTOR_DATA_DIR` 指到临时目录隔离验证;如遇异常启动问题,删除 `%LOCALAPPDATA%\DeLector\instance.lock` 无效(锁随进程退出自动释放),应确认是否有残留 `DeLector.exe`。
+
+### v5.18.0 · 🏗️ 架构与前端单体彻底解耦 + 交互式智能体闭环 + 六道自动化机械闸(PR #131~#161)
自 v5.17.0(`f359ea6`,2026-10-11)起 **128 个提交、25 个 PR** 汇聚为本里程碑版本。主要涵盖三大维度:**核心业务与架构分层解耦**、**交互式智能体循环与裁判闭环**、以及**六道自动化质量守卫机械闸矩阵**。全量测试规模从 1,768 扩展至 **2,438 passed + 3 skipped(净增 670 个测试)**。
diff --git a/FEATURES.md b/FEATURES.md
index ac3f763..4882809 100644
--- a/FEATURES.md
+++ b/FEATURES.md
@@ -8,7 +8,7 @@
---
-## 🌟 核心功能全览(v5.18.0)
+## 🌟 核心功能全览(v5.19.0)
| 能力域 | 一句话 |
| --- | --- |
@@ -267,6 +267,7 @@
| v5.6 – v5.7 | 2026-09 | 听力微训(三模式 + 逐字诊断归因)· 长难句精读工坊(7 维难度评分 + CEFR 带估算) |
| v5.8 – v5.13 | 2026-09 | 词库单一真相化与富字段回填(ADR-0011~0015)· 遇见区分级短文 i+1 就近选材 + 已读队列 · 全库检索(词条 / 例句 / 搭配 / 语料) |
| v5.14 – v5.16 | 2026-10-05 ~ 10-06 | 卡盒去复习化(修掉「白复习」P0)· 前端竞态三修 · TTS 并发闸 · 惰性迁移修复(此前每次 GET 文章都重跑完整 spaCy)· localhost 守卫加固 |
+| **v5.19.0** | **2026-10-22** | **桌面单实例锁(OS 文件锁判据 · 锁/元数据分文件 · 降级时的身份探测第二腿)· 端口顺延 8000→8002–8010(8001 留给 Go supervisor)· `lan-info` 上报真实端口 · 前端继续解耦(`rd-syntax` / `rd-api` / `cd-state`)· 2584 passed(净增 146 用例)** |
| **v5.18.0** | **2026-10-19** | **单体架构彻底解耦(database/routes 拆解 · index.html/writer.js/reader.js 模块化)· 交互式智能体循环(ADR-0023 / 裁判终审 / AI 润色)· 裁判集扩充与真题回测(ADR-0024)· 六道自动化机械闸矩阵落地 · 2438 passed(净增 670 用例)** |
| **v5.17.0** | **2026-10-11** | **首个原生桌面壳(pywebview + 系统托盘 + 统一退出收尸)· 数据落点外置 `%LOCALAPPDATA%\DeLector` · 更新可见性(顶栏 chip + `/api/update/check`)· 发版门守卫(tag == `v`+`APP_VERSION`)· 真启产物闸 / 前端跨边界契约闸 · spaCy 惰性加载(冷启动 3123 → 2128ms)** |
diff --git a/README.md b/README.md
index ee111ef..cb96e90 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
# DeLector · 德语欧标沉浸精读与考点剖析工作台
-
+
-
+
@@ -20,21 +20,22 @@
## 📦 多平台下载发布包 (Downloads)
-> 🚀 **v5.18.0 最新版本亮点(2026-10-19)**:
-> - **单体架构彻底分层解耦**:后端核心 `database.py` 拆解为纯 façade 与领域子模块,`routes/main.py` 拆为 6 个领域独立路由;前端 `workbench.html` 模块化,`writer.js`(`wr-format.js` / `wr-api.js`)与 `reader.js`(`rd-syntax.js`)纯逻辑闭包提取并绝对冻结具名导出面。
-> - **零构建 SPA 静态装配**:`static/index.html` 拆解为 `layout/`、`views/`、`modals/` 独立模板组件,由轻量同步机械闸守护,兼顾开发模块化与零打包首屏防闪烁。
-> - **交互式智能体循环与裁判闭环**:接入模型驱动流转、确定性裁判终审、AI 润色与对齐;A1 裁判集扩充至语法层(主谓一致、动词支配格、主句倒装 V2、冠词词形等)并完成 60 局配对真题回测。
-> - **自动化机械闸矩阵**:新增分层契约闸(词法分词器)、反向依赖闸、Mock 漂移闸、禁止静默降级闸等 6 道自动化防线。全量测试扩展至 **2,438 passed + 3 skipped(净增 670 个测试)**,探针 **31/31** 保持零漂移。
+> 🚀 **v5.19.0 最新版本亮点(2026-10-22)**:
+> - **桌面单实例锁**:同一台机器只允许一个 DeLector —— 判据是 OS 文件锁(`%LOCALAPPDATA%\DeLector\instance.lock`)而不是「端口上有没有东西」。双击第二次会直接告诉你「已在运行(版本 / 进程 / 端口)」,不再出现两个窗口抢同一个学习数据库。
+> - **端口自动顺延**:8000 被其它程序占用时自动落到 8002–8010(8001 留给 Go supervisor),并在日志里写明换了哪个端口;显式 `--port` 仍按你指定的来,不顺延。
+> - **手机配对拿到真实端口**:`/api/wb/lan-info` 改为上报真实监听端口 —— 顺延后手机端不再按错的 8000 去连。
+> - **前端单体继续解耦**:`reader.js` 抽出 `rd-syntax.js`(纯语法层)与 `rd-api.js`(持久化层),`cards.js` 的 15 个模块级可变状态收进纯状态容器 `cd-state.js`;导出面全程冻结。
+> - 全量测试 **2,584 passed + 2 skipped(净增 146 个测试)**,探针 **31/31** 零漂移,六道机械闸全绿。
>
> 📱 **Android 用户注意**:改动含 `static/` 的版本需**覆盖安装**才生效。
> 📜 完整版本变更细节见 [CHANGELOG.md](CHANGELOG.md)。
| 平台 | 版本 | 说明 | 下载通道 |
| ------------------ | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
-| 🪟 **Windows x64** | `v5.18.0` | 原生桌面窗口 + 系统托盘,免装 Python,解压双击 `DeLector.exe`;**需 Win10+ 与 WebView2 运行时**(Win11 自带;Win10 装不上时的表现见「已知限制」);首次启动把数据放到 `%LOCALAPPDATA%\DeLector` | [下载 ZIP 包 (GitHub Releases)](https://github.com/ROM4n2/DeLector/releases/tag/v5.18.0) |
-| 🍎 **macOS** | `v5.18.0` | 解压运行 `start` 脚本,全自动启动服务与默认浏览器 | [下载 TAR.GZ 包 (GitHub Releases)](https://github.com/ROM4n2/DeLector/releases/tag/v5.18.0) |
-| 🐧 **Linux x64** | `v5.18.0` | 全发行版通用,解压运行 `start` 即可使用 | [下载 TAR.GZ 包 (GitHub Releases)](https://github.com/ROM4n2/DeLector/releases/tag/v5.18.0) |
-| 📱 **Android** | `v5.18.0` | 内嵌 Python 运行时与 spaCy 离线模型,单机独立运行;**支持 arm64-v8a**,CI 钉死签名 keystore 并验签(可覆盖升级)。 | [下载 APK 安装包 (GitHub Releases)](https://github.com/ROM4n2/DeLector/releases/tag/v5.18.0) |
+| 🪟 **Windows x64** | `v5.19.0` | 原生桌面窗口 + 系统托盘,免装 Python,解压双击 `DeLector.exe`;**需 Win10+ 与 WebView2 运行时**(Win11 自带;Win10 装不上时的表现见「已知限制」);首次启动把数据放到 `%LOCALAPPDATA%\DeLector` | [下载 ZIP 包 (GitHub Releases)](https://github.com/ROM4n2/DeLector/releases/tag/v5.19.0) |
+| 🍎 **macOS** | `v5.19.0` | 解压运行 `start` 脚本,全自动启动服务与默认浏览器 | [下载 TAR.GZ 包 (GitHub Releases)](https://github.com/ROM4n2/DeLector/releases/tag/v5.19.0) |
+| 🐧 **Linux x64** | `v5.19.0` | 全发行版通用,解压运行 `start` 即可使用 | [下载 TAR.GZ 包 (GitHub Releases)](https://github.com/ROM4n2/DeLector/releases/tag/v5.19.0) |
+| 📱 **Android** | `v5.19.0` | 内嵌 Python 运行时与 spaCy 离线模型,单机独立运行;**支持 arm64-v8a**,CI 钉死签名 keystore 并验签(可覆盖升级)。 | [下载 APK 安装包 (GitHub Releases)](https://github.com/ROM4n2/DeLector/releases/tag/v5.19.0) |
---
@@ -165,6 +166,7 @@ DeLector/
| 版本 | 发布日期 | 里程碑概要 |
| :--- | :--- | :--- |
+| **v5.19.0** | 2026-10-22 | 桌面单实例锁(OS 文件锁判据,双击第二次不再起第二个进程)· 端口自动顺延 8000→8002–8010(8001 留给 supervisor)· `lan-info` 上报真实端口 · 前端 `reader.js`/`cards.js` 继续解耦(`rd-syntax`/`rd-api`/`cd-state`)· 2584 passed |
| **v5.18.0** | 2026-10-19 | 单体架构与前端视图全面解耦(database/routes 拆解 · index.html/writer.js/reader.js 模块化)· 交互式智能体循环(ADR-0023 / 裁判终审 / AI 润色)· 裁判集扩充与真题回测(ADR-0024)· 六道自动化机械闸矩阵落地 · 2438 passed(净增 670 用例) |
| **v5.17.0** | 2026-10-11 | 首个原生桌面壳(pywebview + 系统托盘 + 统一退出收尸)· 数据落点外置 `%LOCALAPPDATA%\DeLector` · 更新可见性(顶栏 chip + `/api/update/check`)· 发版门守卫(tag == `v`+`APP_VERSION`)· 真启产物闸 / 前端跨边界契约闸 · spaCy 惰性加载 |
| **v5.16.0** | 2026-10-06 | 惰性迁移性能缺陷根治(消除单篇 GET spaCy N+1 重算)· CI 必需运行时清单显式化 · localhost 守卫加固 |
diff --git a/SECURITY.md b/SECURITY.md
index d4d3958..77ace24 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -11,7 +11,7 @@ DeLector 项目高度重视系统的安全性、个人数据隐私与离线优
| 版本 | 是否支持安全更新 |
| :--- | :--- |
| **v5.18.x** | :white_check_mark: 支持中 |
-| < v5.18.0 | :x: 不再维护(请升级至最新版本) |
+| < v5.19.0 | :x: 不再维护(请升级至最新版本) |
---
@@ -41,7 +41,7 @@ DeLector 项目高度重视系统的安全性、个人数据隐私与离线优
发送详细的安全分析报告至维护者邮箱:`beianderen@gmail.com`。
### 报告时请包含以下信息:
-- 受影响的版本号(如 v5.18.0);
+- 受影响的版本号(如 v5.19.0);
- 漏洞类型(如 XSS、越权、SSRF、内存破坏等);
- 详细的概念验证(PoC)或复现步骤;
- 潜在的危害分析与建议的修复方案(若有)。
diff --git a/WORKMEMORY/PROJECT_OVERVIEW.md b/WORKMEMORY/PROJECT_OVERVIEW.md
index 33022f3..ddd6586 100644
--- a/WORKMEMORY/PROJECT_OVERVIEW.md
+++ b/WORKMEMORY/PROJECT_OVERVIEW.md
@@ -23,7 +23,8 @@
## 当前状态
-- **最新发布 v5.18.0(2026-10-19)** —— **单体架构与前端视图全面解耦**(`database.py` 拆解为领域模块、`routes/main.py` 拆为 6 个领域路由;`workbench.html` 模块化、`index.html` 拆解为 layout/views/modals 并由轻量同步闸守护;`writer.js`/`reader.js` 纯逻辑闭包提取并冻结导出面)**+ 交互式智能体闭环**(ADR-0023 / 确定性裁判终审 / AI 润色与对齐)**+ A1 裁判集扩充与真题配对回测**(ADR-0024 / 60 局真跑标定真 token 与轮次分布)**+ 六道自动化机械闸矩阵落地**(分层契约闸 · 反向依赖闸 · Mock 漂移闸 · 禁止静默降级闸等)。全量测试从 v5.17.0 的 1,768 增长至 **2,438 passed + 3 skipped**(净增 670 条测试),探针 **31/31** 零漂移。
+- **最新发布 v5.19.0(2026-10-22)** —— **桌面单实例锁 + 端口顺延 + 前端继续解耦**。判据从「端口上有没有东西」换成 OS 文件锁(`%LOCALAPPDATA%\DeLector\instance.lock` + `instance.json` 分离,避免 POSIX `os.replace` 让锁静默失效);唯一判定点 `start.claim_instance`,`start.main()` 与桌面壳共用;端口候选 `8000, 8002–8010`(8001 留给 Go supervisor),自动顺延 / 显式 `--port` 不顺延;锁降级时以 `/api/health` 身份探测作第二腿;`/api/wb/lan-info` 报真实端口。桌面壳退出码新增 3 = 端口或实例冲突(六种 `reason` 各有获批文案)。同版含 `reader.js`→`rd-syntax`/`rd-api` 与 `cards.js`→`cd-state` 的前端解耦。全量 **2584 passed + 2 skipped**。详见 `CHANGELOG.md` 与 `docs/agents/architecture.md`「桌面单实例与端口顺延」。
+- **历史发布 v5.18.0(2026-10-19)** —— **单体架构与前端视图全面解耦**(`database.py` 拆解为领域模块、`routes/main.py` 拆为 6 个领域路由;`workbench.html` 模块化、`index.html` 拆解为 layout/views/modals 并由轻量同步闸守护;`writer.js`/`reader.js` 纯逻辑闭包提取并冻结导出面)**+ 交互式智能体闭环**(ADR-0023 / 确定性裁判终审 / AI 润色与对齐)**+ A1 裁判集扩充与真题配对回测**(ADR-0024 / 60 局真跑标定真 token 与轮次分布)**+ 六道自动化机械闸矩阵落地**(分层契约闸 · 反向依赖闸 · Mock 漂移闸 · 禁止静默降级闸等)。全量测试从 v5.17.0 的 1,768 增长至 **2,438 passed + 3 skipped**(净增 670 条测试),探针 **31/31** 零漂移。
- **历史发布 v5.17.0(2026-10-11)** —— **首个原生桌面壳**(`pywebview` 走系统 WebView2 + `pystray` 托盘 + splash + 统一退出收尸;`v5.16.0` 里 `desktop.py` 根本不存在)**+ 首次数据落点外置**(默认 `%LOCALAPPDATA%\DeLector` + 一次性迁移,修掉唯一真会丢东西的摩擦「升级时解压覆盖 = 学习记录全空且零提示」)**+ 更新可见性上桌面端**(chip + `/api/update/check`)**+ 发版门守卫**(tag 必须 == `v`+`APP_VERSION`)**+ 真启产物闸 / 前端跨边界契约闸**(PR #111~#129,111 个提交;完整条目见 `CHANGELOG.md` 含回滚段)。已装用户拿不到那 6 条可感知修复(`static/` 内嵌在产物里),随本版到达。**known-issues 已明文进发行说明**:~2s 端口/身份校验(本版新引入,不当提速卖点)、`launch.log` 已知整行丢失(≈3%,整行非半行)、Android `versionCode` 不可回滚(`51700`)、Win10 需先装 WebView2。
- **上一版 v5.16.0(2026-10-06)** —— **修掉长期潜伏的性能缺陷 + 三处守卫加固**(PR #108/#109/#110)。**每次 GET 文章都重跑完整 spaCy**(Fog 2 根因:判据写死 `"3.4.0"` 而写入端两条路径都写 `"3.5.0"` ⇒ 判据恒真 ⇒ 惰性迁移退化成每次迁移;修法=`PROCESSED_JSON_VERSION` 单一真相源,语义完整保留)。**CI 必需运行时清单显式化**(`rm` 全仓不存在、`bash` 判为平台相关,两者均不进清单)。**localhost 守卫加固**(关掉三个「将来会漏」的洞:字面 `router` 变量名、per-router prefix、闸调用别名;allowlist 20 条逐字未动)。**安全 posture:内网可信**(不加固 LAN 写端点)。Fog 3 的 16 处形式统一经裁决不做(其收益论证已被实测推翻)。
- **测试 / 门禁基线(截至 v5.17.0)**:全量 **1768 passed + 2 skipped**(分半:非 server **1471** + `test_server` **297**);`ruff check .` 零告警;`mypy --follow-imports=skip tests`(**121** files)、`mypy --strict delector tools`(**87** files)、入口面 `--platform win32 --strict`(**4** files:`desktop.py package_windows.py conftest.py start.py`)零错误;`tools/*.mjs` 探针 **29/29** 零漂移;`test_writer_mobile.py`(发版守护)**30 passed**;**六条守卫**:`test_probe_wiring_guard.py`(防漏接线 5)、`test_probe_json_contract.py`(契约冻结 5)、`test_localhost_guard.py`(受保护路由集合 20 条 allowlist 7)、`test_get_endpoints_with_writes.py`(带写 GET 白名单 7)、`test_ci_hardening.py`(CI 必需运行时清单 33)、`test_app_version_source.py`(发版门 tag↔`APP_VERSION` 等 21)。
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 32953f5..e866dc6 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -5,8 +5,8 @@ plugins {
// 版本号只维护一个源头:git tag。CI 从 tag 推导后通过环境变量注入,
// 本地构建无环境变量时用 fallback 默认值。
-def releaseVersionName = System.getenv("DELECTOR_VERSION_NAME") ?: "5.18.0"
-def releaseVersionCode = (System.getenv("DELECTOR_VERSION_CODE") ?: "51800").toInteger()
+def releaseVersionName = System.getenv("DELECTOR_VERSION_NAME") ?: "5.19.0"
+def releaseVersionCode = (System.getenv("DELECTOR_VERSION_CODE") ?: "51900").toInteger()
// 钉死的签名 keystore。CI 把它从 secret 解到 $RUNNER_TEMP(永不进工作树),
// 本地开发与 fork 没有这个环境变量,签名配置整段不生效、回落到 AGP 自动生成的
diff --git a/delector/core/version.py b/delector/core/version.py
index 0092d70..ff67629 100644
--- a/delector/core/version.py
+++ b/delector/core/version.py
@@ -12,7 +12,7 @@
__all__ = ["APP_VERSION", "version_code"]
# 与 android/app/build.gradle:8 的 versionName fallback 同口径:不带 'v' 前缀。
-APP_VERSION: str = "5.18.0"
+APP_VERSION: str = "5.19.0"
# 每段版本号只有两位空间(minor/patch < 100),故步进 100 / 10000。
_MINOR_STEP = 100
diff --git a/static/index.html b/static/index.html
index 76ad612..7c5165a 100644
--- a/static/index.html
+++ b/static/index.html
@@ -37,7 +37,7 @@
title="点击检查更新"
role="button"
tabindex="0"
- >System · v5.18.0System · v5.19.0