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
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,33 @@ jobs:
- name: Build sdist and wheel
run: python -m build

- name: Compare static root API with wheel
run: |
# This schema-v2 source scan does not import optional dependencies.
python - <<'PY'
from pathlib import Path
import subprocess
import sys

wheels = sorted(Path("dist").glob("fincore-*.whl"))
if len(wheels) != 1:
raise SystemExit(f"expected exactly one fincore wheel in dist/, found {len(wheels)}")
subprocess.run(
[
sys.executable,
"scripts/snapshot_public_api.py",
"--source-root",
".",
"--wheel",
str(wheels[0]),
"--surface",
"fincore",
"--compare",
],
check=True,
)
PY

- name: Check dist
run: twine check dist/*

Expand Down
27 changes: 27 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,33 @@ jobs:
- name: Verify candidate digest
run: python scripts/check_release_candidate.py --dist dist/ --manifest dist/release-manifest.json

- name: Compare static root API with candidate wheel
run: |
# This schema-v2 source scan does not import optional dependencies.
python - <<'PY'
from pathlib import Path
import subprocess
import sys

wheels = sorted(Path("dist").glob("fincore-*.whl"))
if len(wheels) != 1:
raise SystemExit(f"expected exactly one fincore wheel in dist/, found {len(wheels)}")
subprocess.run(
[
sys.executable,
"scripts/snapshot_public_api.py",
"--source-root",
".",
"--wheel",
str(wheels[0]),
"--surface",
"fincore",
"--compare",
],
check=True,
)
PY

- name: Verify release consistency
run: python scripts/check_release_consistency.py --dist dist/

Expand Down
17 changes: 2 additions & 15 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,7 @@ repos:
entry: mypy
args:
- --ignore-missing-imports
- fincore/core
- fincore/constants
- fincore/metrics
- fincore/plugin
- fincore/data
- fincore/optimization
- fincore/attribution
- fincore/report
- fincore/risk
- fincore/simulation
- fincore/utils
- fincore/viz
- fincore/empyrical.py
- fincore/tearsheets
- fincore/pyfolio.py
- fincore
language: system
pass_filenames: false
files: ^fincore/
22 changes: 20 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
# Changelog

All notable changes to Fincore are documented here. This changelog reports version **0.5.1.dev0**, the current development version; **0.5.0** is the latest release.
All notable changes to Fincore are documented here. This changelog reports version **0.5.1**, the current release; **0.5.0** is the preceding breaking unified-core release.

The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and version labels follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [0.5.1] - 2026-09-20

### Fixed

- **Quality gates** — repaired the MyPy pre-commit hook so it checks the
current `fincore` package rather than paths removed by the 0.5 cutover.
- **Reproducible packaging contracts** — excluded pytest's repository-local
temporary staging directory from clean-source copies used by wheel and sdist
contract tests.
- **Static root contract** — made the source-only public-API scanner recognise
the explicitly exported `fincore.__version__` value, allowing the source and
built wheel root namespaces to be compared without importing optional
dependencies.

### Changed

- Release validation now treats the frozen 0.4 API snapshot as historical
compatibility evidence and validates the 0.5 root namespace through the
schema-v2 static contract instead. No public API or runtime behavior changed.

## [0.5.0] - 2026-09-01

Expand Down
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ authors:
email: "yunjinqi@gmail.com"
repository-code: "https://github.com/cloudQuant/fincore"
license: MIT
version: 0.4.0.dev0
version: 0.5.1
keywords:
- quantitative-finance
- performance-analytics
Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# fincore | Quantitative Performance & Risk Analytics

<p align="center">
<img src="https://img.shields.io/badge/version-0.5.0-blueviolet.svg" alt="Version 0.5.0"/>
<img src="https://img.shields.io/badge/version-0.5.1-blueviolet.svg" alt="Version 0.5.1"/>
<img src="https://img.shields.io/badge/status-Beta-orange.svg" alt="Status: Beta"/>
<img src="https://img.shields.io/badge/python-3.11%2B-brightgreen.svg" alt="Python 3.11+"/>
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="MIT License"/>
Expand All @@ -22,10 +22,11 @@ Pyfolio, and Alphalens, but rebuilds them as one low-coupling core rather than
three package-shaped APIs. The public contract is organized by domain and each
capability has one canonical implementation path.

Version **0.5.0** is a deliberately breaking release. It does **not**
provide `fincore.empyrical`, `fincore.pyfolio`, `fincore.alphalens`, flat root
metric functions, compatibility aliases, or façade classes. Update imports to
the focused domain modules described in the [migration guide](docs/MIGRATION.md).
Version **0.5.1** is a quality-maintenance release that preserves the
deliberately breaking 0.5.0 public contract. It does **not** provide
`fincore.empyrical`, `fincore.pyfolio`, `fincore.alphalens`, flat root metric
functions, compatibility aliases, or façade classes. Update imports to the
focused domain modules described in the [migration guide](docs/MIGRATION.md).

| Domain | Use it for | Canonical examples |
| --- | --- | --- |
Expand Down
2 changes: 1 addition & 1 deletion THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Do not hand-edit the JSON block without updating the paired evidence in
"schema_version": 2,
"project": {
"name": "fincore",
"version": "0.5.1.dev0",
"version": "0.5.1",
"license": "MIT"
},
"empyrical": {
Expand Down
54 changes: 54 additions & 0 deletions docs/plans/2026-09-20-cq-2026-09-quality-stabilization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# CQ-2026-09 质量稳态迭代计划

## 目标与边界

| 字段 | 结论 |
| --- | --- |
| 候选基线 | `master@2354bb5` 的隔离工作树;不使用原工作区的未提交治理改动。 |
| 目标 | 修复失效的本地质量门、隔离测试构建输入、建立 0.5 根命名空间的静态契约检查,并让发布流水线验证这些证据。 |
| 不变性 | 不修改 `fincore/` 运行时代码、数值结果、异常语义、公开 API、依赖解析或性能目标。 |
| 发布目标 | `0.5.1`(补丁版本);不重新解释 0.5.0 的破坏性 API 决策。 |
| 非目标 | 兼容层恢复、旧 0.4 API fixture 重写、清理原工作区的忽略工件、改变用户拥有的未提交文件。 |

## 已验证的发现

| ID | 发现 | 状态 | 处置 |
| --- | --- | --- | --- |
| CQ-01 | pre-commit 的 MyPy 参数仍指向已删除目录,hook 退出而不是检查当前包。 | `PASS`(独立复验) | 改为一次 `mypy --ignore-missing-imports fincore`,关闭文件名自动追加。 |
| CQ-02 | 打包合同测试复制源码时会纳入仓库内 `.pytest_tmp`,可能造成递归复制/竞态。 | `PASS`(独立复验) | 两个 staging ignore 列表加入该目录,并有断言。 |
| CQ-03 | `check_quality_snapshot.py --allow-snapshot-output-commit` 在干净候选基线通过。 | `PASS` | 把它作为候选证据,不把输出型快照提交误判为陈旧。 |
| CQ-04 | `public-api-0.4.0.dev0.json` 是原子切换前的 schema-v1 历史投影;它与 0.5 源码不同是预期结果。 | `EXPECTED_HISTORICAL` | 不覆盖、不删除;发布门改用 0.5 的 schema-v2 源码/轮子契约。 |
| CQ-05 | schema-v2 扫描根包时遗漏 `__all__` 明确导出的动态 `__version__` 赋值。 | `PASS`(独立复验) | 静态解析器与回归用例已修复;不接触包运行时代码。 |
| CQ-06 | 隔离 Ruff 扫描有 37 个 C901 热点、5 个 F401 和 6 个 E402。 | `DEFERRED` | 生产源码重构必须先取得缺失的项目开发规范与逐模块行为基线。 |
| CQ-07 | `AGENTS.md` 引用的 `.joyincode/rules/backend.md`、`frontend.md` 在候选基线不存在。 | `BLOCKED` | 在规则恢复或项目负责人明确 waiver 前,不进行 `fincore/` 源码重构。 |

## 实施编排

| 阶段 | 修改范围 | 验收证据 | 状态 |
| --- | --- | --- | --- |
| Q1:本地类型门 | `.pre-commit-config.yaml` | `pre-commit run mypy --all-files` 为 0 | `PASS` |
| Q2:构建输入隔离 | 两个 `tests/packaging/` 文件 | 在仓库外 `--basetemp` 的 20 个打包合同用例通过 | `PASS` |
| Q3:根 API 静态契约 | `scripts/snapshot_public_api.py`、质量/契约测试 | 当前源根和 wheel 根快照均可生成,源/轮一致 | `PASS`(本地) |
| Q4:CI/CD 绑定 | `ci.yml`、`publish.yml` 的最小必要门 | tag 版本、静态契约、构建候选和已有 release 证据均 fail-closed | `PASS`(本地;远端待运行) |
| Q5:在线文档与版本 | `pyproject.toml`、CHANGELOG、MkDocs、README、release note | `0.5.1` 一致且 `mkdocs build --strict` 通过 | `PASS`(本地;部署待运行) |
| Q6:发布验收 | 候选 commit、tag、GitHub CI/Docs/Publish 运行、PyPI 项目页 | 所有本地与远端门 `PASS`;否则明确 `NO-GO` | `PENDING` |

## 不变性与验收矩阵

| 用例 | 命令/证据 | 通过条件 |
| --- | --- | --- |
| AC-01 | `conda run -n base … python -m pre_commit run mypy --all-files` | hook 实际检查 `fincore` 且 exit 0。 |
| AC-02 | `pytest tests/packaging/test_release_consistency.py tests/packaging/test_wheel_contents.py --basetemp <outside-repo>` | staging source 不含 `.pytest_tmp`;全部节点通过。 |
| AC-03 | `snapshot_public_api.py --source-root . --surface fincore` | 仅语法扫描即可产出 schema-v2 根契约;不导入可选依赖。 |
| AC-04 | 构建后的 `--source-root . --wheel <candidate> --surface fincore --compare` | 源码与候选 wheel 的根公开契约逐字一致。 |
| AC-05 | `pytest -m 'not integration_online' --basetemp <outside-repo>` | 非在线回归完整通过;网络型检查单独报告。 |
| AC-06 | `python -m mkdocs build --strict`、release consistency、twine、wheel-consumer | 文档与可发布工件一致。 |
| AC-07 | tag 的 GitHub CI、Docs 与 Publish-to-PyPI 运行;PyPI `0.5.1` 元数据 | 远端证据全部成功后才宣布发布。 |

## 风险控制与放行条件

1. `public-api-0.4.0.dev0.json` 仅保留为历史发现输入;不能用“更新 fixture”掩盖 0.5 公共契约变化。
2. Q3 只允许测试工具和合同文件变化;任何 `fincore/` 源码差异立即停止并重新评审。
3. Q4 不复制已有的完整 CI;只增加当前缺失且能从候选工件验证的 fail-closed 门。
4. Q6 之前必须有干净候选提交、精确 tag/版本一致性、完整 CI 与文档构建成功。PyPI trusted publishing 失败时状态为 `NO-GO`,不得声称已发布。
5. CQ-06/CQ-07 未关闭前,本迭代不把“降低复杂度”作为已完成成果;它们将作为后续、规则齐全且逐模块基线完备的源码重构迭代。
Loading
Loading