Skip to content

Commit fdbb90c

Browse files
Pigbibicodex
andcommitted
feat: project bounded execution receipts
Co-Authored-By: Codex <noreply@openai.com>
1 parent 3a9cda9 commit fdbb90c

7 files changed

Lines changed: 458 additions & 9 deletions

File tree

docs/execution_evidence_runtime_projection.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ that reason every projected record sets `target_data` and `target_execution` to
2626
`target_execution_evidence_missing`. The projection never emits an autonomous
2727
paper/shadow recommendation or a live approval.
2828

29+
## 可选执行回执
30+
31+
新 runtime 可以在原始 `runtime_report.v1` 中附带
32+
`qsl_execution_receipt.v1`。它只允许九个固定结果:未到期、无订单、风控拦截、
33+
已提交、券商确认、部分成交、成交、需对账或失败;同时只保留最小的券商确认状态和
34+
时间。它没有账户、订单号、标的、价格、数量、持仓、资金、错误原文或凭证。
35+
36+
投影器只接受与 runtime report 的平台、策略、40 位 revision、执行通道完全一致,且
37+
内容摘要和时间窗口都有效的回执。缺失、旧格式、篡改或不一致的回执不会被推断为成功:
38+
缺失时仍为 `pending`;失败/需对账时为 `unavailable`;其余有效回执只把“该次结果
39+
已被记录”标为 `verified`。无论哪种情况,推荐仍是 `parked`,不会产生 paper、canary
40+
或实盘授权。
41+
2942
Reports older than its bounded freshness window (36 hours by default), or more
3043
than five minutes in the future, are discarded. The output's `generated_at`
3144
retains the oldest accepted report timestamp rather than the collector time, so

python/scripts/execution_evidence_projection.py

Lines changed: 175 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
SOURCE_SCHEMA_VERSION = "qsl_execution_evidence_source_snapshot.v1"
2929
RUNTIME_REPORT_SCHEMA_VERSION = "runtime_report.v1"
30+
EXECUTION_RECEIPT_SCHEMA_VERSION = "qsl_execution_receipt.v1"
3031
_PLATFORM_ALIASES = {
3132
"alpaca": "alpaca",
3233
"binance": "binance",
@@ -44,6 +45,41 @@
4445
_REVISION = re.compile(r"^[0-9a-f]{40}$")
4546
_IDENTIFIER = re.compile(r"^[A-Za-z0-9._=-]{1,128}$")
4647
_FORBIDDEN_TEXT = re.compile(r"(?:secret|token|password|credential|api[_-]?key|account|order|fill|position|capital)", re.IGNORECASE)
48+
_EXECUTION_RECEIPT_ID = re.compile(r"^execution-receipt\.[0-9a-f]{32}$")
49+
_EXECUTION_RECEIPT_OUTCOMES = frozenset(
50+
{
51+
"not_due",
52+
"no_action",
53+
"risk_blocked",
54+
"submitted",
55+
"broker_acknowledged",
56+
"partially_filled",
57+
"filled",
58+
"reconciliation_required",
59+
"failed",
60+
}
61+
)
62+
_EXECUTION_RECEIPT_CONFIRMATIONS = frozenset(
63+
{
64+
"not_applicable",
65+
"not_observed",
66+
"acknowledged",
67+
"partially_filled",
68+
"filled",
69+
"reconciliation_required",
70+
}
71+
)
72+
_EXECUTION_RECEIPT_OUTCOME_CONFIRMATIONS = {
73+
"not_due": frozenset({"not_applicable"}),
74+
"no_action": frozenset({"not_applicable"}),
75+
"risk_blocked": frozenset({"not_applicable"}),
76+
"submitted": frozenset({"not_observed"}),
77+
"broker_acknowledged": frozenset({"acknowledged"}),
78+
"partially_filled": frozenset({"partially_filled"}),
79+
"filled": frozenset({"filled"}),
80+
"reconciliation_required": frozenset({"reconciliation_required"}),
81+
"failed": frozenset({"not_applicable", "not_observed", "reconciliation_required"}),
82+
}
4783

4884

4985
class ExecutionEvidenceProjectionError(ValueError):
@@ -143,14 +179,23 @@ def _project_runtime_report(report: Mapping[str, Any]) -> tuple[dict[str, Any],
143179
raise ExecutionEvidenceProjectionError("runtime_report_release_unattested")
144180

145181
observed_at = _report_timestamp(report)
182+
execution_receipt = _project_execution_receipt(
183+
report.get("execution_receipt"),
184+
platform=platform,
185+
strategy_profile=profile,
186+
strategy_revision=revision,
187+
execution_mode=execution_mode,
188+
report_observed_at=observed_at,
189+
)
146190
deployment_id = _deployment_id(
147191
platform=platform,
148192
deploy_target=report.get("deploy_target"),
149193
service_name=report.get("service_name"),
150194
strategy_profile=profile,
151195
environment=execution_mode,
152196
)
153-
return {
197+
target_execution, reason_code = _execution_evidence_from_receipt(execution_receipt)
198+
deployment = {
154199
"deployment_id": deployment_id,
155200
"strategy": {
156201
"candidate_id": profile,
@@ -163,13 +208,101 @@ def _project_runtime_report(report: Mapping[str, Any]) -> tuple[dict[str, Any],
163208
"evidence": {
164209
"strategy": "verified",
165210
"target_data": "pending",
166-
"target_execution": "pending",
211+
"target_execution": target_execution,
167212
},
168213
"recommendation": {
169214
"code": "parked",
170-
"reason_code": "target_execution_evidence_missing",
215+
"reason_code": reason_code,
171216
},
172-
}, observed_at
217+
}
218+
if execution_receipt is not None:
219+
deployment["execution_receipt"] = execution_receipt
220+
return deployment, observed_at
221+
222+
223+
def _project_execution_receipt(
224+
value: object,
225+
*,
226+
platform: str,
227+
strategy_profile: str,
228+
strategy_revision: str,
229+
execution_mode: str,
230+
report_observed_at: datetime,
231+
) -> dict[str, str] | None:
232+
"""Project one exact, privacy-safe outcome receipt from a runtime report.
233+
234+
The report itself remains the source of identity. Any receipt that does
235+
not match its platform, strategy revision and lane is discarded instead of
236+
being used to make execution look verified.
237+
"""
238+
239+
if value is None:
240+
return None
241+
receipt = _mapping(value, "runtime_report_execution_receipt_invalid")
242+
expected_fields = {
243+
"schema_version",
244+
"receipt_id",
245+
"platform",
246+
"strategy_profile",
247+
"strategy_revision",
248+
"execution_mode",
249+
"outcome",
250+
"broker_confirmation",
251+
"observed_at",
252+
}
253+
if set(receipt) != expected_fields or receipt.get("schema_version") != EXECUTION_RECEIPT_SCHEMA_VERSION:
254+
raise ExecutionEvidenceProjectionError("runtime_report_execution_receipt_invalid")
255+
receipt_platform = _PLATFORM_ALIASES.get(str(receipt.get("platform") or "").strip().lower())
256+
receipt_profile = _identity(receipt.get("strategy_profile"), "runtime_report_execution_receipt_invalid")
257+
receipt_revision = str(receipt.get("strategy_revision") or "").strip()
258+
receipt_mode = str(receipt.get("execution_mode") or "").strip()
259+
outcome = str(receipt.get("outcome") or "").strip()
260+
confirmation = str(receipt.get("broker_confirmation") or "").strip()
261+
receipt_id = str(receipt.get("receipt_id") or "").strip()
262+
receipt_at = _receipt_timestamp(receipt.get("observed_at"))
263+
if (
264+
receipt_platform != platform
265+
or receipt_profile != strategy_profile
266+
or receipt_revision != strategy_revision
267+
or receipt_mode != execution_mode
268+
or not _REVISION.fullmatch(receipt_revision)
269+
or outcome not in _EXECUTION_RECEIPT_OUTCOMES
270+
or confirmation not in _EXECUTION_RECEIPT_CONFIRMATIONS
271+
or confirmation not in _EXECUTION_RECEIPT_OUTCOME_CONFIRMATIONS[outcome]
272+
or not _EXECUTION_RECEIPT_ID.fullmatch(receipt_id)
273+
):
274+
raise ExecutionEvidenceProjectionError("runtime_report_execution_receipt_invalid")
275+
expected_id = _execution_receipt_id(
276+
platform=receipt_platform,
277+
strategy_profile=receipt_profile,
278+
strategy_revision=receipt_revision,
279+
execution_mode=receipt_mode,
280+
outcome=outcome,
281+
broker_confirmation=confirmation,
282+
observed_at=_timestamp(receipt_at),
283+
)
284+
if receipt_id != expected_id:
285+
raise ExecutionEvidenceProjectionError("runtime_report_execution_receipt_invalid")
286+
if receipt_at > report_observed_at + timedelta(minutes=5) or receipt_at < report_observed_at - timedelta(hours=24):
287+
raise ExecutionEvidenceProjectionError("runtime_report_execution_receipt_timestamp_mismatch")
288+
return {
289+
"outcome": outcome,
290+
"broker_confirmation": confirmation,
291+
"observed_at": _timestamp(receipt_at),
292+
}
293+
294+
295+
def _execution_evidence_from_receipt(
296+
receipt: Mapping[str, str] | None,
297+
) -> tuple[str, str]:
298+
if receipt is None:
299+
return "pending", "target_execution_evidence_missing"
300+
outcome = receipt["outcome"]
301+
if outcome == "reconciliation_required":
302+
return "unavailable", "target_execution_reconciliation_required"
303+
if outcome == "failed":
304+
return "unavailable", "target_execution_receipt_failed"
305+
return "verified", "target_execution_receipt_observed"
173306

174307

175308
def _mapping(value: object, error_code: str) -> Mapping[str, Any]:
@@ -199,6 +332,44 @@ def _report_timestamp(report: Mapping[str, Any]) -> datetime:
199332
raise ExecutionEvidenceProjectionError("runtime_report_timestamp_invalid")
200333

201334

335+
def _receipt_timestamp(value: object) -> datetime:
336+
if not isinstance(value, str) or not value.strip():
337+
raise ExecutionEvidenceProjectionError("runtime_report_execution_receipt_invalid")
338+
try:
339+
parsed = datetime.fromisoformat(value.strip().replace("Z", "+00:00"))
340+
except ValueError as exc:
341+
raise ExecutionEvidenceProjectionError("runtime_report_execution_receipt_invalid") from exc
342+
if parsed.tzinfo is None or parsed.utcoffset() is None:
343+
raise ExecutionEvidenceProjectionError("runtime_report_execution_receipt_invalid")
344+
return parsed.astimezone(UTC).replace(microsecond=0)
345+
346+
347+
def _execution_receipt_id(
348+
*,
349+
platform: str,
350+
strategy_profile: str,
351+
strategy_revision: str,
352+
execution_mode: str,
353+
outcome: str,
354+
broker_confirmation: str,
355+
observed_at: str,
356+
) -> str:
357+
payload = {
358+
"schema_version": EXECUTION_RECEIPT_SCHEMA_VERSION,
359+
"platform": platform,
360+
"strategy_profile": strategy_profile,
361+
"strategy_revision": strategy_revision,
362+
"execution_mode": execution_mode,
363+
"outcome": outcome,
364+
"broker_confirmation": broker_confirmation,
365+
"observed_at": observed_at,
366+
}
367+
digest = hashlib.sha256(
368+
json.dumps(payload, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode("utf-8")
369+
).hexdigest()
370+
return f"execution-receipt.{digest[:32]}"
371+
372+
202373
def _deployment_id(
203374
*,
204375
platform: str,

python/tests/test_execution_evidence_projection.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,33 @@ def _report(self, *, finished_at: str = "2026-08-25T16:00:00Z") -> dict[str, obj
4747
"artifacts": {"runtime_report_cloud_uri": "gs://must-not-be-projected"},
4848
}
4949

50+
def _execution_receipt(self, *, outcome: str = "filled") -> dict[str, str]:
51+
confirmation = {
52+
"filled": "filled",
53+
"failed": "not_observed",
54+
"reconciliation_required": "reconciliation_required",
55+
}[outcome]
56+
observed_at = "2026-08-25T16:00:00Z"
57+
return {
58+
"schema_version": "qsl_execution_receipt.v1",
59+
"receipt_id": projection._execution_receipt_id(
60+
platform="longbridge",
61+
strategy_profile="soxl_soxx_trend_income",
62+
strategy_revision="a" * 40,
63+
execution_mode="paper",
64+
outcome=outcome,
65+
broker_confirmation=confirmation,
66+
observed_at=observed_at,
67+
),
68+
"platform": "longbridge",
69+
"strategy_profile": "soxl_soxx_trend_income",
70+
"strategy_revision": "a" * 40,
71+
"execution_mode": "paper",
72+
"outcome": outcome,
73+
"broker_confirmation": confirmation,
74+
"observed_at": observed_at,
75+
}
76+
5077
def test_projects_only_attested_identity_and_keeps_execution_pending(self):
5178
snapshot = projection.build_execution_evidence_source_snapshot(
5279
[self._report()],
@@ -91,6 +118,46 @@ def test_rejects_unattested_or_lane_mismatched_reports_without_claiming_executio
91118
"runtime_report_release_unattested",
92119
])
93120

121+
def test_projects_a_matching_minimal_execution_receipt_without_order_details(self):
122+
report = self._report()
123+
report["execution_receipt"] = self._execution_receipt()
124+
125+
snapshot = projection.build_execution_evidence_source_snapshot(
126+
[report],
127+
source_id="runtime-reports",
128+
now=datetime(2026, 8, 25, 16, 5, tzinfo=UTC),
129+
)
130+
131+
deployment = snapshot["deployments"][0]
132+
self.assertEqual(deployment["evidence"]["target_execution"], "verified")
133+
self.assertEqual(deployment["recommendation"], {
134+
"code": "parked",
135+
"reason_code": "target_execution_receipt_observed",
136+
})
137+
self.assertEqual(deployment["execution_receipt"], {
138+
"outcome": "filled",
139+
"broker_confirmation": "filled",
140+
"observed_at": "2026-08-25T16:00:00Z",
141+
})
142+
serialized = json.dumps(snapshot, sort_keys=True)
143+
for forbidden in ("receipt_id", "must-not-be-projected", "account_ids", "api_token", "gs://"):
144+
self.assertNotIn(forbidden, serialized)
145+
146+
def test_rejects_a_tampered_execution_receipt_without_claiming_execution(self):
147+
report = self._report()
148+
receipt = self._execution_receipt()
149+
receipt["outcome"] = "submitted"
150+
report["execution_receipt"] = receipt
151+
152+
snapshot = projection.build_execution_evidence_source_snapshot(
153+
[report],
154+
source_id="runtime-reports",
155+
now=datetime(2026, 8, 25, 16, 5, tzinfo=UTC),
156+
)
157+
158+
self.assertEqual(snapshot["data_status"], "unavailable")
159+
self.assertIn("runtime_report_execution_receipt_invalid", snapshot["errors"])
160+
94161
def test_keeps_only_the_latest_report_per_deployment(self):
95162
older = self._report(finished_at="2026-08-25T15:00:00Z")
96163
latest = self._report(finished_at="2026-08-25T16:00:00Z")

0 commit comments

Comments
 (0)