Skip to content

metrics: typed report_result_invalid for overflow; annualised metrics under one day are null with a reason (rev 3.1, CR-202) - #450

Closed
luisleo526 wants to merge 12 commits into
mainfrom
cx/metrics-overflow
Closed

luisleo526 wants to merge 12 commits into
mainfrom
cx/metrics-overflow

Conversation

@luisleo526

Copy link
Copy Markdown
Collaborator

Metrics overflow: revision 3.1 (CR-202, for 1.6.2 final)

CR-202, for the 1.6.2 final. Under revision 3 there is no catch-all null for overflowing report metrics. The owner-approved clarification REVISION 3.1 (L6 ruling 10-20) adds one defined exception:

  • Annualised metrics (cagr, calmar) over a span under one day: null with a named reason, diagnostics.annualization.<field>. The run completes.
  • Any other overflowing report metric, including annualised ones over a span of one day or more: the typed report_result_invalid fault.

The pinned neutral-report digest in tests/test_native_anchored_legs.cpp is re-pinned to the lab-harvested value 17583267501122239024 (rj-20261019t210726-6fa3f7). The 7-minute fixture's cagr/calmar now become null by the rule; nothing else in the digest moved.

Proof 10 (rj-20261019t215306-ac540a, squash tree == c901d07): preflight; release 961/961; debug 942/942; docs.
This head merges main 7bd065c (v1.6.2-rc.1): only VERSION and docker/run_failure_codes_diff.json change. The delta steps and ONE full parity sweep run on this head.

Squash body: see the commit message file of the lane.

🤖 Generated with Claude Code

luisleo526 and others added 12 commits October 19, 2026 23:32
…s (CR-202)

Report metrics that overflowed (buy_hold_return, cagr and every other
product, ratio, sum or annualisation in engine_metrics.cpp, engine_report.cpp,
runner/report.hpp and docker/run_json.py) were silently serialised as null.
Revision 3 Appendix A:82-83 and :93 allow null only for a defined absence, so
an overflowed value now raises the typed report_result_invalid
(reason nonfinite) at the computation that produced it.

- src/report_value_invalid.hpp: one finite-value guard on the existing
  coded-failure mechanism.
- engine_metrics.cpp / engine_report.cpp / runner/report.hpp / run_json.py:
  checked reductions and ratios; defined-domain nulls unchanged.
- tests/ownership/probes/report/buy_hold_return_overflow: the c18_3 probe,
  pinned in the manifest and its checker.
- tests/test_metrics_overflow.cpp: native boundary and absence controls for
  all 87 floating metrics; Python serializer expectations updated.

Not yet run on the lab: the red-first proof and the gate follow.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ix 1)

The kernel guard for the monthly Sharpe and Sortino ratios carried the public
keys metrics.equity.sharpe_tv / sortino_tv as string literals, which the
kernel-residuals preflight check rejects (src/engine_metrics.cpp:327). The
kernel now names its own fields (sharpe_monthly / sortino_monthly) and the
public wire layer maps the last path segment of a report_result_invalid
args.field with the existing EQUITY_REPORT_KEYS, so the wire still shows
sharpe_tv / sortino_tv as revision 3 Appendix A requires.

Native tests expect the native field; Python tests keep the public name on the
wire, and fields outside the monthly aliases pass through unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ix 2)

The first lab proof of the overflow guard failed 22 existing tests:

- A settlement-row failure (engine_invariant, PF_NATIVE_E_RUN_FAILED) was
  turned into PF_NATIVE_E_STATE by the C wrapper when the partial report of
  the failed run then overflowed. strategy_native_run_v1 now keeps the earlier
  typed failure and its run-failed status when fill_report rethrows exactly
  that failure; a report overflow after a successful run still records the
  report_result_invalid detail (revision 3 Appendix A:87, section 2).
- Fixtures whose annualised CAGR (or Calmar) really overflows binary64 now
  expect the typed report_result_invalid failure (Appendix A:83, A:93). The
  expected field is predicted from the frozen equity_stats_reference walk,
  never from the observed exception; finite controls keep every original
  assertion, and the overflow-free twin of each shortened fixture is kept.
- test_metrics_overflow: the two return_sum tapes overflow earlier, in the
  runup percentage; their expected fields follow that trace and isolated
  shared-sum cases cover the helper.
- test_metrics_overflow is registered after the evidence-pinned lines of
  tests/CMakeLists.txt (private_fixture_reads).

The ownership probe files and manifest are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ix 2

docs/adr/0001-kernel-adapter-boundary.md:764 cited strategy_configure_native_ext_result_v1
at native_c_host.cpp:4066; the report-failure precedence change above it moved
the definition to :4091 (check_doc_anchors, preflight stage doc-anchors).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Proof 4 stopped in the Release build: two test hosts called the private
PineStrategyHost::present_report from a derived class. The tests now observe
the engine's own data through the public report read (or, for the amended
trade row, the two protected section helpers on a genuine derived instance)
and predict the rejected metric from the frozen equity_stats_reference walk
after the call. No production header, access or behaviour changes.

Also: self-contained include and inline template in report_failure_checks.hpp,
and include/read guards for the historical-header (R4D harvest) configurations
of the quiet-bar, brackets and anchored-legs tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…d calmar (CR-202 fix 4)

L6 ruled option (b): the annualised metrics cagr and calmar are a DEFINED
absence (null with the named reason span_too_short_to_annualize) when the
measured span is under one day; an overflow on a span of one day or more, and
every non-annualised overflow, stays the typed report_result_invalid fault.

- Measured span: last minus first equity-curve point time_ms (UTC epoch
  milliseconds, the bar clock), unsigned difference; under one day means
  strictly less than 86,400,000 ms. The test runs before the ratio and pow.
- One policy list, docker/report_annualization.def, included by the C++ metric
  walk (src/metrics_annualization.hpp) and read by run_json.py, so the span and
  the field list exist in one place.
- The reason is written outside the revision-3 digest: diagnostics.annualization
  in the public report, only when the rule fired and the metric is null.
- docs/design/selected-window-wire-v1.3-revision-3.1.md and
  docs/pages/report-schema.md state the clarification.
- Tests: the short-span fixtures of fix 2/3 go back to completing with null;
  new cases cover 7 minutes, 86,399,999 ms, exactly one day, an overflow at one
  day and at several days, and the non-annualised buy_hold_return overflow on a
  sub-day span; the wire test shows the reason only when the rule fired.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… return (CR-202 fix 4b)

Proof 6 failed two ways:
- scripts/check_selected_window_ownership.py reads build_report_dict as ONE
  returned dict literal; the revision 3.1 change had turned it into
  out = {...}; return out. The builder returns the literal again and merges
  the diagnostics entry inside it through a small helper.
- run_json.py read docker/report_annualization.def at import, but the product
  and the ownership box ship docker/*.py without data files, so every run
  printed nothing. run_json.py now carries the two policy facts (86,400,000 ms;
  cagr and calmar) itself, and a test pins them to the .def the native metric
  walk includes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…sion 3.1 helpers (CR-202 fix 4c)

tests/ownership/test_manifest_checker.py compiles only a named set of pure
functions and assignments out of docker/run_json.py; build_report_dict now
calls _annualization_block / _annualization_diagnostics and reads the two
policy constants, so the extraction set names them. Doc citations into
docker/run_json.py and tests/test_adapter_quiet_bar.cpp are refreshed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…kes (CR-202 fix 4d)

test_metrics_overflow.cpp handed a ReportC to live::native_report_json, which takes the pf_report_t twin (proof 8 release build). ReportC mirrors pf_report_t member for member; the test casts like src/native_c_host.cpp does at the C boundary and a static_assert on the sizes keeps the cast honest.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…CR-202)

kNeutralReportDigest folds report.metrics. The fixture is a seven-minute +8% run, so under revision 3.1 its cagr and calmar are the defined absence (span under one day) where main computed finite values; the metrics bytes move by that rule only, trades and hashes are unchanged. The new value is the lab-measured digest printed by job rj-20261019t210726-6fa3f7, applied under CORE-TOP's fixture authority of 10-20 05:48.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Brings the branch up to the release commit: VERSION and docker/run_failure_codes_diff.json (metadata of the rc.1 cut). No other file differs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@luisleo526

Copy link
Copy Markdown
Collaborator Author

Superseded by #451: one squash commit ad0804f on main da9403f (same content + #444's docs.yml), because check_doc_reverts.py judges each commit by its own message and this branch is never force-pushed.

@luisleo526 luisleo526 closed this Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant