Repository navigation
run_json: a report never carries a bare NaN or Infinity token (EN-133) - #384
Merged
Merged
Conversation
… and summary values fail as report_result_invalid (EN-133) json.dump ran with allow_nan=True and the trades list and summary carried raw float() values, so a non-finite number became a bare NaN/Infinity token that a strict JSON parser rejects. - _validate_report_result: after the report dictionary is built (selected and ordinary run), the eight doubles of every trade and the five summary doubles must be finite, else RunFailure report_result_invalid with an engine-literal field (trades.<leaf> / summary.<leaf>) and reason nonfinite. - allow_nan=False at the report writers and the printed capabilities and validation-only documents. A value that still reaches a writer fails while the document is in memory (report_post_execution_failed, serialization, behind the acknowledged phase boundary). The failure-line writer keeps its default: its args are not universally sanitized. - Metrics, equity curve and recorded outputs keep _num (non-finite -> null). - tests/test_run_json_nonfinite_report.py: all 13 leaves on both paths, the writer backstop, finite-output byte identity against the frozen base producer, null controls; the native-host process rows skip unless a host library is supplied. One sentence in report-schema.md. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…esult_invalid (EN-133)
Without --run-phase-fd there is no execution boundary, so the strict
json.dump(allow_nan=False) refusal on the ordinary report used to surface as
harness_internal_error. It now fails with report_result_invalid, field
"report.document", reason "nonfinite", for the encoder's exact refusal
messages only. With an acknowledged boundary it stays
report_post_execution_failed{phase: serialization}; other ValueErrors, and the
capabilities and --validate-window-only documents, keep harness_internal_error.
The old unphased pin is renamed and corrected, with a circular-document control.
One sentence in report-schema.md.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
| """ | ||
|
|
||
| import contextlib | ||
| import ctypes |
…ines (EN-133) tests/private_fixture_reads.evidence pins the line of the pooc close-fill-view registration in tests/CMakeLists.txt (3251). The EN-133 test registration adds three lines above it, so the check_private_fixture_reads guard and its control row (test_private_fixture_reads, test_private_fixture_reads_controls) fail on the moved line. The pinned line and the line numbers quoted in its reason text move by three; nothing else changes. Found on the first spot-box run of the sibling EN-134 slice, which moves the same lines. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…m a lane commit (EN-133) test_finite_report_bytes_match_base compares the finite report bytes of the changed docker/run_json.py with the frozen producer it replaces. The pin named a commit of the lane's own local base (26296bb) that does not exist in the engine's history, so on every verify host the fixture's `git show` failed (exit 128) and the test errored at setup: test_run_json_nonfinite_report failed in the release, debug, sanitizers and kernel steps of the first lab-remote run of this PR. The pin is now the engine main commit this change sits on (7da487a, docker/run_json.py there is byte-identical to the file at the commit the change was authored on, 9da4c98; both hash to the same sha256). Nothing else changes. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
luisleo526
marked this pull request as ready for review
October 13, 2026 09:40
luisleo526
added a commit
that referenced
this pull request
Oct 14, 2026
* docs: the 1.6.0-rc.1 changelog entry The changelog section for the first candidate of 1.6.0, tag v1.6.0-rc.1 on commit 04607c1: the selected-window contract revision 1.3 and the typed failures behind it (#388), finite trade and summary doubles in every report writer (#384, #388), the favicon of the API documentation (#355), the removed CI time limits and command-line options (#378), and the tests, CI and benchmarks changes since 1.5.0 (#375, #376, #377, #382, #383, #385, #386, #387). The entry is inserted above the 1.5.0 section; no existing line changes. The date is the UTC date of the tag. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> * README: link the apex pineforge.dev, not the www host The README header link pointed at https://www.pineforge.dev. The product site lives at the apex, https://pineforge.dev; the www host is being redirected to it, and search indexes both forms today. One link changed, nothing else. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> * CHANGELOG: add the selected-window mode and typed failures to the 1.5.0 notes Two things that are in the v1.5.0 tag were missing from its notes: the opt-in selected-window mode with retained pre-roll bars (#363, already in v1.5.0-rc.1) and the typed failures that docker/run_json.py raises for exceptions from the selected-window helper modules (_typed_failure). The new subsection "Additions to the 1.5.0 notes" only states what the code and the #363 text say, with the same "development-only parity candidate" limits (contract revision 1.3-parity, no claim of complete value-ownership coverage, cannot be promoted to stable). It adds lines and changes none. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> --------- Co-authored-by: Claude Sonnet 5.5 <[email protected]>
luisleo526
added a commit
that referenced
this pull request
Oct 20, 2026
#456) * report: fail with report_result_invalid when a report metric overflows; annualise cagr proportionally under one day, with a named form flag (CR-202, owner W-73) Squash of the CR-202 work on engine main 0212cab. Its single parent is engine main, as the GitHub squash writes it. What changes - A report metric whose computed value overflows (buy_hold_return, every other report metric) fails the run with the typed failure report_result_invalid, reason nonfinite, instead of writing null. - Proportional annualisation (owner ruling W-73; docs/design/selected-window-wire-v1.3-revision-3.1.md, docs/pages/report-schema.md). span_years = (last bar time - first bar time) / (365.25 * 86400 * 1000 ms), as before. Under one day (span_ms < 86 400 000) cagr = (E/E0 - 1) * (1/span_years) * 100, the simple proportional form, always, and calmar = cagr / max_equity_drawdown_pct follows. From one day on cagr is the compounded form, unchanged; only if that value is not finite the simple form is used instead (a rare ranking inversion across the one-day boundary, documented). sharpe_bar and sortino_bar use the same formulas on every span and are never absent by span; sharpe_tv and sortino_tv are unchanged. No null for these four on a valid run; every other non-finite value is the typed report_result_invalid. - The public report names the form under diagnostics.annualization (outside the 135 digest leaves): cagr = "simple_proportional_subday" or "simple_proportional_overflow", and calmar carries the same string when it is not null. The writer derives it from the curve it holds; for a run of one day or more whose compounded form is not finite and whose first mark is not proven to be the initial capital the cagr is still the simple value and only the overflow flag is absent (documented; a runtime export of the capital is carded for a later release). The frozen report structures gain no member. - C host (src/native_c_host.cpp): a report failure after a completed run is recorded and returns E_STATE, a report failure after an earlier latched failure returns E_RUN_FAILED with that failure; in both the caller's report struct is defined (zeroed local report freed on a throw) instead of half written. - The native runner keeps its own report format: undefined metrics stay the string "NaN" (runner/README.md); only an infinite value is the typed failure there. - Disclosed behaviour: an infinite first-open or last-close price and a non-finite selected anchor are now report_result_invalid (feed admission makes the first unreachable). - kNeutralReportDigest in tests/test_native_anchored_legs.cpp is a lab harvest of the neutral 15 one-minute bar run (its cagr is the simple proportional value on a 14-minute span); the value is taken from the lab log under the fixture authority of CORE-TOP. Documentation sentences reworded or replaced by this change, each named for scripts/check_doc_reverts.py (the sentence's introducing commit, then its first words): - 1bd075c (Trading metrics + equity curve in backtest report, #31): "See the per-field doxygen in `<pineforge/pineforge.h>` for the exact rule on every field." in docs/pages/report-schema.md, now "in `<pineforge/pineforge.h>` for the existing field domains; the rule below is authoritative" for cagr on a span under one day and when its compounded form overflows. - 1b08c61 (EN-133, #384): "net_pnl, avg_trade, best_trade, worst_trade and max_drawdown, must be finite or the run fails with report_result_invalid, field naming the corresponding trades.<leaf> or summary.<leaf> and reason equal to nonfinite." in docs/pages/report-schema.md, extended with win_rate_pct and the equity-curve equity and open_profit leaves. - 5691220 (ADR-0001 rewritten against HEAD): "compute_equity_stats answers its all-NaN value with no error" in docs/adr/0001-kernel-adapter-boundary.md, now "answers its defined empty-curve metrics with no error". - 375795a (shard the ownership manifest-controls test, CR-196): "(9, 9, 9 and 8 for shards 1 to 4 of 4: 8, 8, 8 and 7 cases plus the guard)" and "The separate cheap test_shards_partition_every_case_exactly_once guard runs in every shard" in tests/ownership/README.md, now "(10, 10, 10 and 10 for shards 1 to 4 of 4: 9 cases plus the guard in each)" and "Each shard also runs the cheap test_shards_partition_every_case_exactly_once guard". - file:line citation digits only, wording kept: ADR-0001 (the "inclusive bar metrics" bullet, the "Reporting." bullet, the strategy_configure_native_v1 row, the magnifier_sub_bars_total row), native-feature-parity rows RP2, RP4, S1b and "A zoned chart-day memo", native-refactor-progress (the quiet-bar cost check and the every_gate_is_taken_both_ways citation), native-engine ("MUST mirror" citation of compute_equity_stats). Documentation sentences the history of this branch introduced and later reworded or removed (none of them is deleted by this squash against main): - c9ba507 introduced "Report arithmetic that produces a nonfinite value, including overflow, fails with `report_result_invalid` and reason `nonfinite`." and "The engine checks computed values before they can become a metric absence." in docs/pages/report-schema.md, both kept. - eba8561 (revision 3.1) introduced "The constant policy list is in `docker/report_annualization.def`, shared by the native metric walk and public report writer." in docs/design/selected-window-wire-v1.3-revision-3.1.md; e963e5e (the earlier squash of this work) introduced the revision 3.1 note's null-rule sentences ("The short-span absence applies to cagr, calmar, sharpe_bar and sortino_bar", "Calmar carries the short-span reason with CAGR even when the curve also has zero drawdown", "span_too_short_to_annualize") and the report-schema.md ones; the owner ruling W-73 withdrew that null rule and they are reworded or replaced in this change. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> * tests: keep the base assertion CHECK(!std::isnan(e.cagr)) in test_metrics_l4d (twin parity) scripts/check_twin_parity.py requires every assertion literal of the base test_metrics to survive in its switched-route twin test_metrics_l4d.cpp. The proportional-annualisation rewrite of test_equity_stats_drawdown_walk dropped `CHECK(!std::isnan(e.cagr));`, so the source-guard-twin-parity stage failed and the verifier-tests stage of preflight read `not-started` for every later stage. The literal is back, unchanged and still true (cagr is defined on every valid run); the reference comparison below it stays. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> * tests: re-pin kNeutralReportDigest to the lab harvest of the proportional-annualisation report The neutral run (15 one-minute bars, a 14-minute span, KernelRecorded) now reports a finite simple proportional cagr (10000 to about 10005, no drawdown) and main's formulas for sharpe_bar and sortino_bar, so nothing is absent and its report digest moves again from the value pinned for the earlier null rule. Value harvested from lab job rj-20261020t035042-1a9c33 (label cr202-v6c-pre-rel, release step, test_native_anchored_legs:262), log line 2525: "neutral report digest 6216030702059937357, pinned 8139101939074791901". Fixture authority for exactly that printed value: CORE-TOP (2026-10-20 12:29). That test was the only failure of that release run (961 of 962 tests passed). Co-Authored-By: Claude Sonnet 5.5 <[email protected]> --------- Co-authored-by: Claude Sonnet 5.5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
run_json: a report never carries a bare NaN or Infinity token (EN-133)
docker/run_json.pyserialized trade and summary doubles with the standard encoder's default, so a non-finite number (for example a NaNmax_runupfrom a host's lot-excursion answer) could reach the output as a bareNaN/Infinitytoken, which is not JSON.Behaviour
entry_price,exit_price,qty,pnl,pnl_pct,max_runup,max_drawdown,commission) and the five summary doubles are checked after the report is built, in the ordinary and the Selected-window run. A non-finite value fails the run withreport_result_invalid{field: "trades.<leaf>" | "summary.<leaf>", reason: "nonfinite"};fieldis an engine-owned literal, never script text.allow_nan=False), so nothing non-JSON can be written. With an acknowledged execution boundary a writer fault staysreport_post_execution_failed{phase: serialization}; without a boundary (no--run-phase-fd) the encoder's non-finite refusal isreport_result_invalid{field: "report.document", reason: "nonfinite"}; otherValueErrors and the capabilities and--validate-window-onlydocuments keepharness_internal_error.null.docs/pages/report-schema.md. The three codes are already indocker/run_failure_codes.json; the catalog is unchanged.Proof (spot box b1, 2026-10-13, tree identical for these four files to the proven head
3b6b56c1;git diffbetween this branch and that head over the four files is empty)test_trade_leaf_nonfinite,test_summary_leaf_nonfinite,test_writer_backstop, the unphased-writer and ordinary-typed-leaf rows,test_native_net_profit_nonfinite,test_finite_pnls_overflow_summary_drawdown); the four controls (metric and equity stay null, finite report bytes identical to base, engine non-finite args dropped) pass on base.pytest tests/test_run_json_nonfinite_report.py tests/test_run_json_selected_window.py242 passed, 2 skipped (the two native host-library rows, which need a private fixture).ValueErrorconverted).Part of EN-132 (value ownership). This slice is independent of the others and lands early.
🤖 Generated with Claude Code