feat: add Quant OS public status console
This commit is contained in:
@@ -6,7 +6,7 @@ Quant OS 是项目名;`quant60` 是第一版 A 股 Baseline 内核、CLI names
|
|||||||
回测/影子验证,并且所有 G1—G10 都由版本绑定的真实证据支持。
|
回测/影子验证,并且所有 G1—G10 都由版本绑定的真实证据支持。
|
||||||
|
|
||||||
当前结论仍为 `NOT_BASELINE_60`。最近一次标准库全量测试记录为
|
当前结论仍为 `NOT_BASELINE_60`。最近一次标准库全量测试记录为
|
||||||
`Ran 216 tests`、`OK (skipped=6)`;六个 skip 是可选依赖/运行时路径。
|
`Ran 224 tests`、`OK (skipped=6)`;六个 skip 是可选依赖/运行时路径。
|
||||||
该结果证明代码合同,不证明真实平台、券商或合规 Gate。
|
该结果证明代码合同,不证明真实平台、券商或合规 Gate。
|
||||||
|
|
||||||
## Stage 1: 独立仓库与确定性执行
|
## Stage 1: 独立仓库与确定性执行
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ SMOKE_DIR := artifacts/local-smoke
|
|||||||
RESEARCH_DIR := artifacts/research-smoke
|
RESEARCH_DIR := artifacts/research-smoke
|
||||||
PARITY_REPORT := artifacts/mock-parity/report.json
|
PARITY_REPORT := artifacts/mock-parity/report.json
|
||||||
|
|
||||||
.PHONY: test bundle smoke verify research research-verify parity parity-verify doctor notebook-check notebook-smoke local
|
.PHONY: test bundle smoke verify research research-verify parity parity-verify doctor status status-check notebook-check notebook-smoke local
|
||||||
|
|
||||||
test:
|
test:
|
||||||
PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) -m unittest discover -s tests -p '*.py' -q
|
PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) -m unittest discover -s tests -p '*.py' -q
|
||||||
@@ -33,10 +33,16 @@ parity-verify:
|
|||||||
doctor:
|
doctor:
|
||||||
PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) tools/capability_probe.py
|
PYTHONPATH=$(PROJECT_PYTHONPATH) $(PYTHON) tools/capability_probe.py
|
||||||
|
|
||||||
|
status:
|
||||||
|
$(PYTHON) tools/build_public_status.py
|
||||||
|
|
||||||
|
status-check:
|
||||||
|
$(PYTHON) tools/build_public_status.py --check
|
||||||
|
|
||||||
notebook-check:
|
notebook-check:
|
||||||
$(PYTHON) tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb
|
$(PYTHON) tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb
|
||||||
|
|
||||||
notebook-smoke:
|
notebook-smoke:
|
||||||
$(PYTHON) tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb --execute
|
$(PYTHON) tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb --execute
|
||||||
|
|
||||||
local: test bundle smoke verify research research-verify parity parity-verify doctor notebook-check notebook-smoke
|
local: test bundle smoke verify research research-verify parity parity-verify doctor status-check notebook-check notebook-smoke
|
||||||
|
|||||||
@@ -15,6 +15,26 @@ Quant OS 是项目名;`quant60` 是第一版 A 股 Baseline 的 Python
|
|||||||
> [`docs/AUDIT_2026-07-25.md`](docs/AUDIT_2026-07-25.md) 与
|
> [`docs/AUDIT_2026-07-25.md`](docs/AUDIT_2026-07-25.md) 与
|
||||||
> [`gate_scorecard.json`](gate_scorecard.json)。
|
> [`gate_scorecard.json`](gate_scorecard.json)。
|
||||||
|
|
||||||
|
## 在线公开验收面板
|
||||||
|
|
||||||
|
公开只读入口:
|
||||||
|
|
||||||
|
[`https://gomars.fun/quants-strategies/quant-os/status/index.html`](https://gomars.fun/quants-strategies/quant-os/status/index.html)
|
||||||
|
|
||||||
|
页面从 `gate_scorecard.json`、平台矩阵、bundle manifest 和证据文档生成
|
||||||
|
脱敏的构建时快照,逐层展示数据、模型、回测平台、券商影子盘与 G1—G10
|
||||||
|
缺口。它不是实时服务健康页,不发布本地行情、账户状态或运行 artifacts。
|
||||||
|
|
||||||
|
更新权威记录后必须重新生成并检查页面数据:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 tools/build_public_status.py
|
||||||
|
python3 tools/build_public_status.py --check
|
||||||
|
```
|
||||||
|
|
||||||
|
网页源码位于 [`status/`](status/),生成器位于
|
||||||
|
[`tools/build_public_status.py`](tools/build_public_status.py)。
|
||||||
|
|
||||||
## 当前交付边界
|
## 当前交付边界
|
||||||
|
|
||||||
| 路径 | 当前能做什么 | 已有证据 | 仍缺什么 |
|
| 路径 | 当前能做什么 | 已有证据 | 仍缺什么 |
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ saved/current source aggregate、saved/current schema aggregate 和 payload
|
|||||||
schema。`verify-snapshot-decision` 与 data snapshot semantic verifier 承担
|
schema。`verify-snapshot-decision` 与 data snapshot semantic verifier 承担
|
||||||
各自边界的同类职责。
|
各自边界的同类职责。
|
||||||
|
|
||||||
这些合同已有自动测试;最近一次标准库套件为 216 tests、OK、6 个可选路径
|
这些合同已有自动测试;最近一次标准库套件为 224 tests、OK、6 个可选路径
|
||||||
skip。测试结果不能替代 schema 迁移演练、真实平台导出或券商回调认证。
|
skip。测试结果不能替代 schema 迁移演练、真实平台导出或券商回调认证。
|
||||||
|
|
||||||
## Clocks and reconciliation
|
## Clocks and reconciliation
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ A 股 Baseline 内核。它的新增得分必须在授权数据、真实平台
|
|||||||
|
|
||||||
截至 2026-07-26 的实现事实快照:
|
截至 2026-07-26 的实现事实快照:
|
||||||
|
|
||||||
- 截至 2026-07-26 的最新标准库全量测试为 `Ran 216 tests`、`OK (skipped=6)`;
|
- 截至 2026-07-26 的最新标准库全量测试为 `Ran 224 tests`、`OK (skipped=6)`;
|
||||||
- JQData raw/factor/pre-close/limits/paused + 每日 PIT `is_st`/指数成员
|
- JQData raw/factor/pre-close/limits/paused + 每日 PIT `is_st`/指数成员
|
||||||
已连接到 semantic snapshot verifier、`snapshot-backtest` 和
|
已连接到 semantic snapshot verifier、`snapshot-backtest` 和
|
||||||
`snapshot-decision`,并拒绝把抓取当日未到 24:00 的日线标成完整收盘,
|
`snapshot-decision`,并拒绝把抓取当日未到 24:00 的日线标成完整收盘,
|
||||||
@@ -152,7 +152,7 @@ A 股 Baseline 内核。它的新增得分必须在授权数据、真实平台
|
|||||||
T 日 Signal 与 T+1 broker fact 绑定、完整 artifact verifier;
|
T 日 Signal 与 T+1 broker fact 绑定、完整 artifact verifier;
|
||||||
- Colab 默认无账号流程,以及显式可选的 JQData/Qlib cell。
|
- Colab 默认无账号流程,以及显式可选的 JQData/Qlib cell。
|
||||||
|
|
||||||
最近一次 216-test suite、两条 Qlib fixture smoke 和 Tushare
|
最近一次 224-test suite、两条 Qlib fixture smoke 和 Tushare
|
||||||
managed-provider 技术 smoke 证明上述代码路径能执行,
|
managed-provider 技术 smoke 证明上述代码路径能执行,
|
||||||
但仍明确不证明:
|
但仍明确不证明:
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
| Path | Intended use | Runtime / prerequisite | Current verified fact | Missing release evidence |
|
| Path | Intended use | Runtime / prerequisite | Current verified fact | Missing release evidence |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| Local synthetic execution | 确定性事件回测、ledger/replay | Python ≥3.10,无账号 | 标准库套件最近记录为 216 tests、OK、6 skip;smoke + exact manifest verifier 可运行 | synthetic 不代表真实市场/收益 |
|
| Local synthetic execution | 确定性事件回测、ledger/replay | Python ≥3.10,无账号 | 标准库套件最近记录为 224 tests、OK、6 skip;smoke + exact manifest verifier 可运行 | synthetic 不代表真实市场/收益 |
|
||||||
| Local synthetic research | PIT/features/walk-forward/Ridge/risk/cost/target | Python ≥3.10,无账号 | research smoke + manifest verifier 可运行 | Ridge 未冻结部署,未做授权长样本 OOS |
|
| Local synthetic research | PIT/features/walk-forward/Ridge/risk/cost/target | Python ≥3.10,无账号 | research smoke + manifest verifier 可运行 | Ridge 未冻结部署,未做授权长样本 OOS |
|
||||||
| JQData ingestion | 授权数据到 canonical immutable snapshot | `jqdatasdk==1.9.8` + 授权账号 | fake-provider 测试覆盖 raw/factor/pre-close/limits/paused/PIT `is_st`/membership、24:00 完整收盘约束和语义 verifier | 尚未登录真实账号保存快照、许可与 lineage |
|
| JQData ingestion | 授权数据到 canonical immutable snapshot | `jqdatasdk==1.9.8` + 授权账号 | fake-provider 测试覆盖 raw/factor/pre-close/limits/paused/PIT `is_st`/membership、24:00 完整收盘约束和语义 verifier | 尚未登录真实账号保存快照、许可与 lineage |
|
||||||
| Snapshot local backtest | PIT membership 的 provider-data 本地回测 | semantic verifier 通过的 snapshot | `snapshot-backtest` 复用 production decision path,输出普通 run manifest | 尚无授权真实 snapshot;仍是本地 fill model |
|
| Snapshot local backtest | PIT membership 的 provider-data 本地回测 | semantic verifier 通过的 snapshot | `snapshot-backtest` 复用 production decision path,输出普通 run manifest | 尚无授权真实 snapshot;仍是本地 fill model |
|
||||||
|
|||||||
+2
-2
@@ -33,9 +33,9 @@
|
|||||||
"current_delivery": {
|
"current_delivery": {
|
||||||
"score": null,
|
"score": null,
|
||||||
"status": "not_scored",
|
"status": "not_scored",
|
||||||
"reason": "Quant OS now has executable local pipelines, real Qlib fixture smokes, a checksummed incomplete Tushare-to-Qlib technical smoke and one completed real JoinQuant hosted smoke, but it has not produced complete adjusted PIT data, the same-input JoinQuant comparison, real QMT, broker-shadow, continuous reconciliation or compliance evidence required to score Baseline 60.",
|
"reason": "Quant OS 已有可执行本地流水线、真实 Qlib fixture smoke、带校验但不完整的 Tushare→Qlib 技术 smoke,以及一次完成的真实聚宽 hosted smoke;但仍缺完整复权 PIT 数据、同输入聚宽比较、真实 QMT、券商影子盘、连续对账与合规证据,因此不能计入 Baseline 60。",
|
||||||
"verified_supporting_facts": [
|
"verified_supporting_facts": [
|
||||||
"The latest standard-library suite completed 216 tests with OK and 6 optional-runtime skips; the CPython 3.12 pyqlib environment completed the same 216 tests with the native Qlib smoke enabled and no skips.",
|
"The latest standard-library suite completed 224 tests with OK and 6 optional-runtime skips; the CPython 3.12 pyqlib environment completed the same 224 tests with the native Qlib smoke enabled and no skips.",
|
||||||
"The JQData adapter maps raw bars, adjustment factor, previous close, daily limits, pause state, PIT is_st and daily PIT index membership into a semantically revalidated snapshot; it rejects an end date that has not reached the provider's complete daily-bar boundary, and only fake-provider tests have been run so far.",
|
"The JQData adapter maps raw bars, adjustment factor, previous close, daily limits, pause state, PIT is_st and daily PIT index membership into a semantically revalidated snapshot; it rejects an end date that has not reached the provider's complete daily-bar boundary, and only fake-provider tests have been run so far.",
|
||||||
"The local snapshot can drive snapshot-backtest and snapshot-decision with manifest verification; it freezes a cross-checked provider trading calendar, and a T-close signal can bind only to a broker observation in the unique next session's Asia/Shanghai [09:00,09:30) window without collapsing the two clocks.",
|
"The local snapshot can drive snapshot-backtest and snapshot-decision with manifest verification; it freezes a cross-checked provider trading calendar, and a T-close signal can bind only to a broker observation in the unique next session's Asia/Shanghai [09:00,09:30) window without collapsing the two clocks.",
|
||||||
"CPython 3.12 with pyqlib 0.9.7 completed a native momentum fixture smoke with 24 signals and saved audited portfolio-table hashes, bounds and recomputed metrics.",
|
"CPython 3.12 with pyqlib 0.9.7 completed a native momentum fixture smoke with 24 signals and saved audited portfolio-table hashes, bounds and recomputed metrics.",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ PYTHONPATH=src:. "${python_bin}" tools/export_mock_parity.py \
|
|||||||
verify \
|
verify \
|
||||||
artifacts/mock-parity/report.json
|
artifacts/mock-parity/report.json
|
||||||
PYTHONPATH=src:. "${python_bin}" tools/capability_probe.py
|
PYTHONPATH=src:. "${python_bin}" tools/capability_probe.py
|
||||||
|
"${python_bin}" tools/build_public_status.py --check
|
||||||
"${python_bin}" tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb
|
"${python_bin}" tools/run_colab_notebook.py notebooks/quant_os_colab.ipynb
|
||||||
"${python_bin}" tools/run_colab_notebook.py \
|
"${python_bin}" tools/run_colab_notebook.py \
|
||||||
notebooks/quant_os_colab.ipynb \
|
notebooks/quant_os_colab.ipynb \
|
||||||
|
|||||||
+2117
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,217 @@
|
|||||||
|
import importlib.util
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
PROJECT = Path(__file__).resolve().parents[1]
|
||||||
|
BUILDER_PATH = PROJECT / "tools" / "build_public_status.py"
|
||||||
|
OUTPUT_PATH = PROJECT / "status" / "status-data.js"
|
||||||
|
INDEX_PATH = PROJECT / "status" / "index.html"
|
||||||
|
PREFIX = "window.QUANT_OS_PUBLIC_STATUS = "
|
||||||
|
|
||||||
|
|
||||||
|
def _load_builder():
|
||||||
|
spec = importlib.util.spec_from_file_location("build_public_status", BUILDER_PATH)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise RuntimeError("cannot import public status builder")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def _read_payload(path=OUTPUT_PATH):
|
||||||
|
content = path.read_text(encoding="utf-8")
|
||||||
|
lines = content.splitlines()
|
||||||
|
if not lines or not lines[0].startswith("// Generated by"):
|
||||||
|
raise AssertionError("generated header is missing")
|
||||||
|
assignment = "\n".join(lines[1:])
|
||||||
|
if not assignment.startswith(PREFIX) or not assignment.endswith(";"):
|
||||||
|
raise AssertionError("unexpected JavaScript wrapper")
|
||||||
|
return json.loads(assignment[len(PREFIX) : -1])
|
||||||
|
|
||||||
|
|
||||||
|
class PublicStatusTests(unittest.TestCase):
|
||||||
|
def test_committed_status_data_has_no_drift(self):
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, str(BUILDER_PATH), "--check"],
|
||||||
|
cwd=PROJECT,
|
||||||
|
check=False,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
self.assertEqual(result.returncode, 0, result.stderr)
|
||||||
|
payload = _read_payload()
|
||||||
|
self.assertEqual(payload["claim"]["status"], "NOT_BASELINE_60")
|
||||||
|
self.assertFalse(payload["claim"]["releaseVerified"])
|
||||||
|
self.assertFalse(payload["claim"]["liveReady"])
|
||||||
|
self.assertEqual(payload["claim"]["gatesPassed"], 0)
|
||||||
|
self.assertEqual(payload["claim"]["gatesTotal"], 10)
|
||||||
|
self.assertTrue(all(gate["status"] == "not_passed" for gate in payload["gates"]))
|
||||||
|
|
||||||
|
def test_public_payload_is_deliberately_non_secret_and_non_local(self):
|
||||||
|
payload = _read_payload()
|
||||||
|
encoded = json.dumps(payload, ensure_ascii=False, sort_keys=True)
|
||||||
|
self.assertNotRegex(
|
||||||
|
encoded,
|
||||||
|
re.compile(r"/(?:Users|home|root|private|var|tmp)/|[A-Za-z]:[\\/]"),
|
||||||
|
)
|
||||||
|
self.assertNotIn("RELEASE_VERIFIED", encoded)
|
||||||
|
self.assertNotIn("LIVE_READY", encoded)
|
||||||
|
lowered = encoded.lower()
|
||||||
|
for marker in (
|
||||||
|
"password",
|
||||||
|
"passwd",
|
||||||
|
"api_key",
|
||||||
|
"private_key",
|
||||||
|
"userdata_path",
|
||||||
|
):
|
||||||
|
self.assertNotIn(marker, lowered)
|
||||||
|
for evidence in payload["evidence"]:
|
||||||
|
self.assertFalse(Path(evidence["path"]).is_absolute())
|
||||||
|
self.assertNotIn("..", Path(evidence["path"]).parts)
|
||||||
|
|
||||||
|
def test_static_page_has_fail_closed_fallback_and_no_local_paths(self):
|
||||||
|
page = INDEX_PATH.read_text(encoding="utf-8")
|
||||||
|
self.assertIn('src="./status-data.js"', page)
|
||||||
|
self.assertGreaterEqual(page.count("STATUS DATA UNAVAILABLE"), 2)
|
||||||
|
self.assertIn("NOT_BASELINE_60", page)
|
||||||
|
self.assertIn("0 / 10", page)
|
||||||
|
self.assertIn("validatePublicData", page)
|
||||||
|
self.assertIn("platform.limitations", page)
|
||||||
|
self.assertNotIn("platform.missingReleaseEvidence", page)
|
||||||
|
self.assertNotRegex(
|
||||||
|
page,
|
||||||
|
re.compile(
|
||||||
|
r"(?:(?<=^)|(?<=[\s\"'=:(]))"
|
||||||
|
r"(?:/(?:Users|home|root|private|var|tmp)/|[A-Za-z]:[\\/])"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.assertNotIn("quant-os/artifacts", page)
|
||||||
|
self.assertNotIn("quant-os/data", page)
|
||||||
|
for tag in re.findall(r"<a\\b[^>]*target=\"_blank\"[^>]*>", page):
|
||||||
|
self.assertIn('rel="noopener noreferrer"', tag)
|
||||||
|
|
||||||
|
def test_models_and_platforms_preserve_evidence_boundaries(self):
|
||||||
|
payload = _read_payload()
|
||||||
|
models = {model["id"]: model for model in payload["models"]}
|
||||||
|
self.assertEqual(
|
||||||
|
set(models),
|
||||||
|
{"portable-momentum-v1", "ridge-challenger", "alpha158-lightgbm"},
|
||||||
|
)
|
||||||
|
self.assertFalse(models["portable-momentum-v1"]["releaseVerified"])
|
||||||
|
self.assertFalse(models["ridge-challenger"]["investmentValueClaim"])
|
||||||
|
self.assertFalse(models["alpha158-lightgbm"]["investmentValueClaim"])
|
||||||
|
self.assertIn("synthetic", models["ridge-challenger"]["role"])
|
||||||
|
self.assertIn("synthetic", models["alpha158-lightgbm"]["limitations"][0])
|
||||||
|
|
||||||
|
platforms = {platform["name"]: platform for platform in payload["platforms"]}
|
||||||
|
self.assertEqual(platforms["JoinQuant hosted bundle"]["status"], "real_runtime")
|
||||||
|
self.assertEqual(platforms["QMT built-in bundle"]["status"], "implemented")
|
||||||
|
self.assertFalse(platforms["XtTrader read-only shadow"]["liveReady"])
|
||||||
|
self.assertIn("fake", platforms["XtTrader read-only shadow"]["claim"].lower())
|
||||||
|
|
||||||
|
def test_freshness_uses_daily_bars_not_the_later_trade_calendar(self):
|
||||||
|
payload = _read_payload()
|
||||||
|
freshness = {item["id"]: item for item in payload["freshness"]}
|
||||||
|
self.assertEqual(freshness["project-audit"]["value"], "2026-07-26")
|
||||||
|
self.assertEqual(
|
||||||
|
freshness["joinquant-hosted-market-window"]["marketDataThrough"],
|
||||||
|
"2024-12-31",
|
||||||
|
)
|
||||||
|
|
||||||
|
tushare = freshness["tushare-daily-market-data"]
|
||||||
|
self.assertEqual(tushare["completedPartitions"], 35)
|
||||||
|
self.assertEqual(tushare["plannedPartitions"], 428)
|
||||||
|
self.assertAlmostEqual(tushare["completionRatio"], 35 / 428, places=6)
|
||||||
|
self.assertEqual(tushare["marketDataThrough"], "1993-10-29")
|
||||||
|
self.assertFalse(tushare["productionReady"])
|
||||||
|
source = (PROJECT / "docs" / "TUSHARE_LOCAL_DATA.md").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
self.assertRegex(source, r"`trade_cal`.*2026")
|
||||||
|
self.assertNotIn("2026", tushare["marketDataThrough"])
|
||||||
|
self.assertIn("daily", tushare["semantics"])
|
||||||
|
|
||||||
|
self.assertEqual(freshness["jqdata-real-snapshot"]["value"], "N/A")
|
||||||
|
self.assertEqual(freshness["jqdata-real-snapshot"]["status"], "blocked")
|
||||||
|
self.assertIsNone(
|
||||||
|
freshness["qmt-broker-real-runtime"]["lastRealRuntimeAt"]
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
freshness["qmt-broker-real-runtime"]["status"], "blocked"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_explicit_timestamp_build_is_deterministic(self):
|
||||||
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
first = Path(temp_dir) / "first.js"
|
||||||
|
second = Path(temp_dir) / "second.js"
|
||||||
|
command = [
|
||||||
|
sys.executable,
|
||||||
|
str(BUILDER_PATH),
|
||||||
|
"--generated-at",
|
||||||
|
"2026-07-26T08:09:10+08:00",
|
||||||
|
]
|
||||||
|
subprocess.run(
|
||||||
|
[*command, "--output", str(first)],
|
||||||
|
cwd=PROJECT,
|
||||||
|
check=True,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
subprocess.run(
|
||||||
|
[*command, "--output", str(second)],
|
||||||
|
cwd=PROJECT,
|
||||||
|
check=True,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
self.assertEqual(first.read_bytes(), second.read_bytes())
|
||||||
|
payload = _read_payload(first)
|
||||||
|
self.assertEqual(payload["generatedAt"], "2026-07-26T00:09:10Z")
|
||||||
|
self.assertEqual(payload["generatedAtSource"], "argument")
|
||||||
|
|
||||||
|
def test_source_date_epoch_controls_timestamp(self):
|
||||||
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
output = Path(temp_dir) / "epoch.js"
|
||||||
|
environment = dict(os.environ)
|
||||||
|
environment["SOURCE_DATE_EPOCH"] = "0"
|
||||||
|
result = subprocess.run(
|
||||||
|
[
|
||||||
|
sys.executable,
|
||||||
|
str(BUILDER_PATH),
|
||||||
|
"--output",
|
||||||
|
str(output),
|
||||||
|
],
|
||||||
|
cwd=PROJECT,
|
||||||
|
env=environment,
|
||||||
|
check=False,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
self.assertEqual(result.returncode, 0, result.stderr)
|
||||||
|
payload = _read_payload(output)
|
||||||
|
self.assertEqual(payload["generatedAt"], "1970-01-01T00:00:00Z")
|
||||||
|
self.assertEqual(payload["generatedAtSource"], "source_date_epoch")
|
||||||
|
|
||||||
|
def test_builder_rejects_unearned_positive_claim(self):
|
||||||
|
module = _load_builder()
|
||||||
|
scorecard = {
|
||||||
|
"claim_policy": {
|
||||||
|
"baseline_threshold": 60,
|
||||||
|
"current_claim": "BASELINE_60",
|
||||||
|
},
|
||||||
|
"current_delivery": {"score": 59},
|
||||||
|
"gates": [{"id": "G1", "status": "not_passed"}],
|
||||||
|
}
|
||||||
|
with self.assertRaises(module.PublicStatusError):
|
||||||
|
module._validate_claim(scorecard)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user