Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions docs/qsl_compat_upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<bundle>.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 收敛路径

Expand Down
58 changes: 36 additions & 22 deletions python/tests/test_internal_dependency_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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/<bundle>.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({})
Expand Down