From 221772c3b3fd2293ef5ba480e2b2c14ca94b7992 Mon Sep 17 00:00:00 2001 From: ranzhigg Date: Wed, 9 Sep 2026 08:30:04 +0800 Subject: [PATCH 1/5] test(architecture): refresh v3 dependency baseline --- docs/architecture-overview.md | 2 +- docs/architecture/optimization-checklist.md | 2 +- tests/fixtures/architecture/dependency-baseline.json | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/architecture-overview.md b/docs/architecture-overview.md index aaac70335..3a5643c77 100644 --- a/docs/architecture-overview.md +++ b/docs/architecture-overview.md @@ -755,7 +755,7 @@ flowchart LR | 指标 | 当前值 | |---|---:| | Python 模块 | 985 | -| 内部导入边 | 8,358 | +| 内部导入边 | 8,359 | | 非平凡 SCC | 1(精确 containment 的 TMDB 移植包环) | | Application / Chain 具体 Adapter 直连 | 0 / 0 | | Direct egress | 53(债务已清零,53 条精确 containment) | diff --git a/docs/architecture/optimization-checklist.md b/docs/architecture/optimization-checklist.md index 933fbf120..5f7aa397e 100644 --- a/docs/architecture/optimization-checklist.md +++ b/docs/architecture/optimization-checklist.md @@ -94,7 +94,7 @@ ARCH-201 至 ARCH-204 均达到实现、验证、提交、推送和远端门禁 | 指标 | 当前值 | 解释 | |---|---:|---| -| 宿主 Python 模块 / 内部依赖边 | 985 / 8,358 | `dependency-baseline.json` 当前快照;分类、下载资源归类、订阅搜索与整理任务恢复模块的受控依赖 | +| 宿主 Python 模块 / 内部依赖边 | 985 / 8,359 | `dependency-baseline.json` 当前快照;分类、下载资源归类、订阅搜索与整理任务恢复模块的受控依赖 | | 非平凡 SCC | 1 | 仅保留精确 containment 的 29 模块 TMDB 移植包环 | | 跨层 DB 边界债务 | 0 | Application、Chain、API、Agent、Runtime、Workflow 到 DB 的受控债务均为零 | | Model/Oper 事务债务 | 0 | 自建 Session、自动事务装饰器、直接 commit/rollback 等基线均为零 | diff --git a/tests/fixtures/architecture/dependency-baseline.json b/tests/fixtures/architecture/dependency-baseline.json index 847a19cf7..df9f61bc4 100644 --- a/tests/fixtures/architecture/dependency-baseline.json +++ b/tests/fixtures/architecture/dependency-baseline.json @@ -1074,8 +1074,8 @@ "runtime_only": true } }, - "edge_count": 8358, - "edge_sha256": "c0785c61259ea746f28fa0e13d65fb72c3d0d438d52bc624177f0787f98f8728", + "edge_count": 8359, + "edge_sha256": "68e8ba94394d9c5b6e77890dde09b5f8916776c39992958460e198f854a1ee19", "edges": [ "app -> app.foundation", "app -> app.foundation.environment", @@ -6617,6 +6617,7 @@ "app.modules.indexer -> app.modules.indexer.spider.yema", "app.modules.indexer -> app.runtime", "app.modules.indexer -> app.runtime.log", + "app.modules.indexer -> app.runtime.settings", "app.modules.indexer -> app.schemas", "app.modules.indexer -> app.schemas.media", "app.modules.indexer -> app.schemas.site", From c018f171d8e0891bffaa571fc7b6162f29afc043 Mon Sep 17 00:00:00 2001 From: ranzhigg Date: Wed, 9 Sep 2026 08:39:34 +0800 Subject: [PATCH 2/5] test(architecture): refresh site complexity ratchet --- tests/fixtures/architecture/complexity-v2-baseline.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/fixtures/architecture/complexity-v2-baseline.json b/tests/fixtures/architecture/complexity-v2-baseline.json index 1a5cfeb18..897946810 100644 --- a/tests/fixtures/architecture/complexity-v2-baseline.json +++ b/tests/fixtures/architecture/complexity-v2-baseline.json @@ -21,7 +21,7 @@ "app/chain/search/media.py:SearchMediaOwner": 522, "app/chain/search/provider.py:SearchProviderOwner": 662, "app/chain/search/subtitle.py:SearchSubtitleOwner": 528, - "app/chain/site.py:SiteChain": 1003, + "app/chain/site.py:SiteChain": 1015, "app/chain/subscribe/create.py:SubscribeCreateOwner": 646, "app/chain/subscribe/match.py:SubscribeMatchOwner": 571, "app/chain/subscribe/refresh.py:SubscribeRefreshOwner": 760, @@ -45,7 +45,7 @@ "app/chain/interaction.py": 1571, "app/chain/message.py": 1984, "app/chain/scraping.py": 2065, - "app/chain/site.py": 1187, + "app/chain/site.py": 1199, "app/chain/transfer/queue.py": 1631, "app/chain/transfer/workflow.py": 1106, "app/chain/workflow.py": 1391 From 6e991171f328802c5d5c5418b70dd41e77ecb700 Mon Sep 17 00:00:00 2001 From: ranzhigg Date: Wed, 9 Sep 2026 10:05:06 +0800 Subject: [PATCH 3/5] fix(architecture): restore ratchet baselines without weakening gates --- app/adapters/external/plugin/client.py | 6 +++- app/chain/site.py | 28 ++++++------------- app/domain/site.py | 14 +++++++++- .../architecture/complexity-v2-baseline.json | 4 +-- .../fixtures/architecture/ruff-baseline.json | 3 -- 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/app/adapters/external/plugin/client.py b/app/adapters/external/plugin/client.py index 1a48473df..d20e081d5 100644 --- a/app/adapters/external/plugin/client.py +++ b/app/adapters/external/plugin/client.py @@ -26,10 +26,14 @@ from app.domain.plugin import ( is_local_plugin_source, is_physical_plugin_id, is_plugin_generation_compatible, - normalize_plugin_market_repo_url as _normalize_plugin_market_repo_url, parse_local_plugin_generation, parse_local_plugin_path, parse_local_plugin_reference, +) +from app.domain.plugin import ( + normalize_plugin_market_repo_url as _normalize_plugin_market_repo_url, +) +from app.domain.plugin import ( split_plugin_market_repo_urls as _split_plugin_market_repo_urls, ) from app.foundation.environment import is_free_threaded_runtime diff --git a/app/chain/site.py b/app/chain/site.py index 9be8ccf2f..62ff7c090 100644 --- a/app/chain/site.py +++ b/app/chain/site.py @@ -470,16 +470,6 @@ class SiteChain(InteractionChainMixin, ChainBase): return False, "Cookie已过期" return False, f"错误:{res.status_code} {res.reason}" - @staticmethod - def __resolve_site_page_url(site_url: str, page_path: Optional[str]) -> str: - """将站点资源声明的相对页面路径解析为绝对地址。""" - page_path = str(page_path or "").strip() - if not page_path: - return site_url - if page_path.startswith(("http://", "https://")): - return page_path - return urljoin(f"{str(site_url).rstrip('/')}/", page_path.lstrip("/")) - def __hddolby_test(self, site: SiteSnapshot) -> Tuple[bool, str]: """ 判断站点是否已经登陆:hddolby @@ -693,7 +683,7 @@ class SiteChain(InteractionChainMixin, ChainBase): logger.warning(f"站点 {domain} 已在黑名单中,不添加站点") return 0, 0, 0, False domain_url = self._cookiecloud_indexer_domain(indexer, domain) - login_url = self.__resolve_site_page_url(domain_url, indexer.get("login_path")) + login_url = site_rules.resolve_page_url(domain_url, indexer.get("login_path")) proxy, response = self._cookiecloud_connect(login_url, cookie, indexer) if response is None: return 0, 0, 1, False @@ -886,16 +876,14 @@ class SiteChain(InteractionChainMixin, ChainBase): state, message = special_test(site_info) else: indexer = SitesHelper().get_indexer(domain) or {} - login_path = indexer.get("login_path") - if login_path: - state, message = self.__test( - replace( - site_info, - url=self.__resolve_site_page_url(site_info.url, login_path), - ) + state, message = self.__test( + replace( + site_info, + url=site_rules.resolve_page_url( + site_info.url, indexer.get("login_path") + ), ) - else: - state, message = self.__test(site_info) + ) # 统计 seconds = (datetime.now() - start_time).seconds if state: diff --git a/app/domain/site.py b/app/domain/site.py index 70d0169f2..ef50470f5 100644 --- a/app/domain/site.py +++ b/app/domain/site.py @@ -1,9 +1,11 @@ +from typing import Optional +from urllib.parse import urljoin + from lxml import etree from app.foundation.dom import DomUtils from app.foundation.url import split_netloc - _SPECIAL_SITE_DOMAINS = ( "u2.dmhy.org", "pt.ecust.pp.ua", @@ -39,6 +41,16 @@ def extract_domain(url: str) -> str: return ".".join(labels[-2:]) +def resolve_page_url(site_url: str, page_path: Optional[str]) -> str: + """将站点资源声明的页面路径解析为绝对地址。""" + page_path = str(page_path or "").strip() + if not page_path: + return site_url + if page_path.startswith(("http://", "https://")): + return page_path + return urljoin(f"{str(site_url).rstrip('/')}/", page_path.lstrip("/")) + + class SiteUtils: """提供站点域名、Cookie 和访问参数处理能力。""" diff --git a/tests/fixtures/architecture/complexity-v2-baseline.json b/tests/fixtures/architecture/complexity-v2-baseline.json index 897946810..1a5cfeb18 100644 --- a/tests/fixtures/architecture/complexity-v2-baseline.json +++ b/tests/fixtures/architecture/complexity-v2-baseline.json @@ -21,7 +21,7 @@ "app/chain/search/media.py:SearchMediaOwner": 522, "app/chain/search/provider.py:SearchProviderOwner": 662, "app/chain/search/subtitle.py:SearchSubtitleOwner": 528, - "app/chain/site.py:SiteChain": 1015, + "app/chain/site.py:SiteChain": 1003, "app/chain/subscribe/create.py:SubscribeCreateOwner": 646, "app/chain/subscribe/match.py:SubscribeMatchOwner": 571, "app/chain/subscribe/refresh.py:SubscribeRefreshOwner": 760, @@ -45,7 +45,7 @@ "app/chain/interaction.py": 1571, "app/chain/message.py": 1984, "app/chain/scraping.py": 2065, - "app/chain/site.py": 1199, + "app/chain/site.py": 1187, "app/chain/transfer/queue.py": 1631, "app/chain/transfer/workflow.py": 1106, "app/chain/workflow.py": 1391 diff --git a/tests/fixtures/architecture/ruff-baseline.json b/tests/fixtures/architecture/ruff-baseline.json index 20ba6e4a5..6f571ebf0 100644 --- a/tests/fixtures/architecture/ruff-baseline.json +++ b/tests/fixtures/architecture/ruff-baseline.json @@ -167,9 +167,6 @@ "app/domain/scraper.py": { "I001": 1 }, - "app/domain/site.py": { - "I001": 1 - }, "app/domain/title.py": { "I001": 1 }, From 2ea04c1687fed1b660337d1d6e5f9d427e42a06e Mon Sep 17 00:00:00 2001 From: ranzhigg Date: Wed, 9 Sep 2026 10:14:13 +0800 Subject: [PATCH 4/5] docs(architecture): sync Ruff quality metric --- docs/architecture/optimization-checklist.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/optimization-checklist.md b/docs/architecture/optimization-checklist.md index 5f7aa397e..a4f39b0be 100644 --- a/docs/architecture/optimization-checklist.md +++ b/docs/architecture/optimization-checklist.md @@ -103,7 +103,7 @@ ARCH-201 至 ARCH-204 均达到实现、验证、提交、推送和远端门禁 | Python 源码量 | 305,884 行 | 排除 `app/plugins/**`;61 个文件超过 1,000 行,11 个超过 2,000 行 | | 长方法 | 290 个超过 80 行 | AST 统计排除 `app/plugins/**`;65 个超过 150 行,21 个超过 250 行 | | 全量 mypy 历史债务 | 9,440 / 513 文件 | Agent API 重构后的现状基线;canonical Facade 与 endpoint 类型边界已补齐,低水位只允许继续下降 | -| Ruff 历史诊断 | 537 | 低水位门禁通过,但规则集只覆盖 `E4/E7/E9/F/I` | +| Ruff 历史诊断 | 536 | 低水位门禁通过,但规则集只覆盖 `E4/E7/E9/F/I` | | 覆盖率固定基线 | Application 80.00%,Domain 80.00% | Chain、Runtime、Agent、Adapter、Startup 未进入包级覆盖率门禁 | ### 3.3 热点文件 From 5f12ffa0c274d38454cc33ca948c43b39bf45cfa Mon Sep 17 00:00:00 2001 From: ranzhigg Date: Wed, 9 Sep 2026 10:27:40 +0800 Subject: [PATCH 5/5] fix(plugin-market): satisfy index request type gate --- app/adapters/external/plugin/client.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/app/adapters/external/plugin/client.py b/app/adapters/external/plugin/client.py index d20e081d5..277e08378 100644 --- a/app/adapters/external/plugin/client.py +++ b/app/adapters/external/plugin/client.py @@ -1161,7 +1161,7 @@ class PluginMarketTransport(metaclass=WeakSingleton): def _remove_index_task( cls, key: tuple[asyncio.AbstractEventLoop, str, str], - task: asyncio.Task[Optional[PluginIndex]], + task: asyncio.Future[Optional[PluginIndex]], ) -> None: """异步索引请求结束后释放事件循环和仓库引用。""" with cls._index_task_lock: @@ -1223,10 +1223,14 @@ class PluginMarketTransport(metaclass=WeakSingleton): key = self._plugin_index_key(repo_url, package_version) with self._index_future_lock: future = self._index_futures.get(key) - owner = future is None - if owner: - future = Future() + if future is None: + future = Future[Optional[PluginIndex]]() self._index_futures[key] = future + owner = True + else: + owner = False + + assert future is not None if not owner: return future.result() @@ -1592,12 +1596,13 @@ class PluginMarketTransport(metaclass=WeakSingleton): ), ) self._index_tasks[task_key] = task - task.add_done_callback( - lambda completed_task, key=task_key: self._remove_index_task( - key, - completed_task, - ) - ) + + def on_index_task_done( + completed_task: asyncio.Future[Optional[PluginIndex]], + ) -> None: + self._remove_index_task(task_key, completed_task) + + task.add_done_callback(on_index_task_done) # 单个调用方取消等待时不能连带取消共享请求。 return await asyncio.shield(task)