From 267655ceff3a98efb077c0a8ec5e48488dedcdfc Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Tue, 25 Aug 2026 20:12:45 +0800 Subject: [PATCH] docs(qsl): separate bundle and rollout version sources Co-Authored-By: Codex --- docs/qsl_compat_upgrade.md | 35 +++++++---- .../tests/test_internal_dependency_matrix.py | 58 ++++++++++++------- 2 files changed, 61 insertions(+), 32 deletions(-) diff --git a/docs/qsl_compat_upgrade.md b/docs/qsl_compat_upgrade.md index f322f17..f340ae4 100644 --- a/docs/qsl_compat_upgrade.md +++ b/docs/qsl_compat_upgrade.md @@ -51,16 +51,31 @@ python scripts/render_qsl_dependency_graph.py --repo-root . --format md - 当前 checker 已识别 `legacy_reason` 和 `live_constraint_files`;`owner` / `expires_at` / `next_action` 已进入 checker warning,用于约束过渡例外的负责人、到期日和下一步动作。 - `--non-strict` 仅用于本地快速预览,不作为发布门禁依据。 -## 当前中心兼容基线 - -- Bundle: `2026.08.0` -- QPK: `3acab1923a97b805b077c85c6c19657be0143bac` -- UsEquityStrategies: `be1a2c9f7c388d4dd78bda6c2dd7ccad0d4b13b4` -- HkEquityStrategies: `72008f44050b0dc7415334535dbe784793180159` -- CnEquityStrategies: `167355385dde4e1a2b9dea4ede7077f9fbda13ce` -- CryptoStrategies: `d2b43112154c35c7b6c2651a240054926a779ae9` -- QuantStrategyPlugins: `c798397d9ca9230e404673d7774bac3d478217dc` -- MarketSignalSources: `956dd380f75aac4ecb6f8443d95619c8a7f1c52b` +## 版本真相边界与受控升级 + +不要在文档中把一个 SHA 解释为“所有平台当前运行版本”。QSL 有三个各司其职的版本来源: + +| 目的 | 唯一来源 | 含义 | +| --- | --- | --- | +| 兼容目标 | `compat/bundles/.toml` | 已发布的兼容 bundle;用于严格仓库校验与回退基线。 | +| 已验证平台实际 pin | `internal_dependency_matrix.json` | 从各 consumer 仓库的 `main` 依赖文件生成;这是平台/策略当前已合入版本的唯一台账。 | +| 下一轮 QPK 候选 | `QuantPlatformKit/QPK_PIN` | 只表示待分阶段推广的候选,不代表任何平台已经升级。 | + +`QPK_PIN` 变更先经过候选安装与依赖检查,再以只改该文件的 PR 进入主分支。随后才按 +`strategy → consumer → aggregate bundle` 顺序创建下游 PR;每一个下游 PR 仍须通过自身 CI, +不会直接触发运行时部署或交易。平台版本与候选不同步时,优先读取 matrix,而不是 bundle +或候选 pin。 + +在同步下游仓库后,用生成器维护和核对实际台账: + +```bash +python3 python/scripts/qslctl.py generate-matrix --projects-root .. --check --strict +python3 python/scripts/qslctl.py generate-matrix --projects-root .. --sync +python3 python/scripts/check_internal_dependency_matrix.py --projects-root .. --strict --require-consumer-files +``` + +第一条命令用于 CI/监测,第二条只在经过下游 CI 的变更需要提交台账时使用。这样 bundle、 +候选和实际运行依赖不会再因重复手工维护而相互矛盾。 ## Phase-2 Transition Warning 收敛路径 diff --git a/python/tests/test_internal_dependency_matrix.py b/python/tests/test_internal_dependency_matrix.py index d091b0d..06e1dfc 100644 --- a/python/tests/test_internal_dependency_matrix.py +++ b/python/tests/test_internal_dependency_matrix.py @@ -99,32 +99,39 @@ def test_current_matrix_matches_local_workspace(self): self.assertEqual(report.missing_files, []) self.assertEqual(report.issues, []) - def test_tracked_qpk_consumer_pins_match_latest_matrix_snapshot(self): - expected_pins_by_consumer = { - "BinancePlatform": "9f7e7f8335e97f83f66677ad5e73254a1a421759", - "CharlesSchwabPlatform": "9f7e7f8335e97f83f66677ad5e73254a1a421759", - "CnEquityStrategies": "54d4ba901ae4e72e09c143c051747b900de55022", - "CryptoStrategies": "54d4ba901ae4e72e09c143c051747b900de55022", - "FirstradePlatform": "9f7e7f8335e97f83f66677ad5e73254a1a421759", - "HkEquityStrategies": "54d4ba901ae4e72e09c143c051747b900de55022", - "InteractiveBrokersPlatform": "9f7e7f8335e97f83f66677ad5e73254a1a421759", - "LongBridgePlatform": "9f7e7f8335e97f83f66677ad5e73254a1a421759", - "UsEquityStrategies": "54d4ba901ae4e72e09c143c051747b900de55022", - "UsEquitySnapshotPipelines": "2341557b8ded834bd41ef5a92111361dbf8844d9", + def test_tracked_qpk_consumer_pins_are_coherent_per_consumer(self): + tracked_consumers = { + "BinancePlatform", + "CharlesSchwabPlatform", + "CnEquityStrategies", + "CryptoStrategies", + "FirstradePlatform", + "HkEquityStrategies", + "InteractiveBrokersPlatform", + "LongBridgePlatform", + "UsEquityStrategies", + "UsEquitySnapshotPipelines", } matrix_pins = check_internal_dependency_matrix.load_matrix(ROOT / "internal_dependency_matrix.json") refs = { (pin.consumer_repo, pin.path): pin.ref for pin in matrix_pins - if pin.consumer_repo in expected_pins_by_consumer and pin.source_repo == "QuantPlatformKit" + if pin.consumer_repo in tracked_consumers and pin.source_repo == "QuantPlatformKit" } - expected_refs = { - (consumer_repo, path): ref - for consumer_repo, ref in expected_pins_by_consumer.items() + expected_keys = { + (consumer_repo, path) + for consumer_repo in tracked_consumers for path in ("pyproject.toml", "uv.lock") } - self.assertEqual(refs, expected_refs) + self.assertEqual(set(refs), expected_keys) + for consumer_repo in tracked_consumers: + consumer_refs = { + refs[(consumer_repo, path)] + for path in ("pyproject.toml", "uv.lock") + } + self.assertEqual(len(consumer_refs), 1) + self.assertRegex(next(iter(consumer_refs)), r"^[0-9a-f]{40}$") uesp_strategy_refs = { (pin.consumer_repo, pin.path): pin.ref @@ -133,12 +140,19 @@ def test_tracked_qpk_consumer_pins_match_latest_matrix_snapshot(self): and pin.source_repo == "UsEquityStrategies" } self.assertEqual( - uesp_strategy_refs, - { - ("UsEquitySnapshotPipelines", path): "180701592efda47d8acd824dca98f3d74a5ee462" - for path in ("pyproject.toml", "uv.lock") - }, + set(uesp_strategy_refs), + {("UsEquitySnapshotPipelines", path) for path in ("pyproject.toml", "uv.lock")}, ) + self.assertEqual(len(set(uesp_strategy_refs.values())), 1) + self.assertRegex(next(iter(uesp_strategy_refs.values())), r"^[0-9a-f]{40}$") + + def test_upgrade_document_distinguishes_target_candidate_and_observed_pins(self): + document = (ROOT / "docs" / "qsl_compat_upgrade.md").read_text(encoding="utf-8") + + self.assertIn("compat/bundles/.toml", document) + self.assertIn("internal_dependency_matrix.json", document) + self.assertIn("QuantPlatformKit/QPK_PIN", document) + self.assertNotRegex(document, r"[0-9a-f]{40}") def test_require_consumer_files_treats_missing_paths_as_issues(self): projects_root = self._make_projects_root({})