Skip to content

cr150 L5: trade-history flag and export - #213

Merged
luisleo526 merged 17 commits into
mainfrom
core/cr150-l5
Oct 24, 2026
Merged

luisleo526 merged 17 commits into
mainfrom
core/cr150-l5

Conversation

@luisleo526

Copy link
Copy Markdown
Contributor

CR-150 lane L5 (engine 1.7.0 flat memory), per DESIGN rev 6 and its PARALLEL-PLAN row. Codegen flags scripts that read strategy.closedtrades.* or opentrades.*, so only those keep the engine's trade history (parity). The lane's REPORT.md has the detail.

Lab: rj-20261018t163011-3e84ef green (pytest, 9848+ tests, against the CR-150 P00 engine tree). It pairs with the engine 1.7.0 release: not to merge before the engine side lands. The gating read by CORE-TOP comes before merge.

🤖 Generated with Claude Code

luisleo526 and others added 17 commits October 18, 2026 23:26
…en branch

A merge commit, no rebase: core/cr150-l5 (codegen PR #213, pairs with the engine 1.7.0 line)
lacked the security fix. The merge was automatic and clean (no conflict).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… not report (CR-276)

The test's hand-written C++ program defined a global static void report(...) and then called report(precalc) after
the generated code's `using namespace pineforge;`. The 1.7 engine declares namespace pineforge::report
(include/pineforge/engine.hpp:65 forward declaration, include/pineforge/report_pages.hpp:12), so the unqualified call
is ambiguous (clock.cpp: "reference to 'report' is ambiguous", lab job 34cad5). Rename the test's helper; no engine or
codegen emitter change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… base is an ancestor of the head (REV-CX r4 F1)

The PR base 54ce4de was not an ancestor of core/cr150-l5 (4b0ff8b): the merge-base was v1.6.3 (3ac6503) and main had gained two commits, so the PR diff showed 1,106 deleted lines it does not intend. This is a merge commit, not a rebase; the automatic merge was clean (five files from main: .github/workflows/promote-baseline.yml, release.yml, test.yml, CONTRIBUTING.md, tests/test_pr_workflows.py). No conflict resolution, no edit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…history setter must not break generated code (red first)

A Pine type named report, report_sink or report_sequence (UDT) compiles on 1.6.3 headers and fails on the 1.7.0 ones ("reference to report is ambiguous"), because the engine now has namespaces of those names and the generated file says `using namespace pineforge;`. The generated constructor also calls the trade-history policy setter, and a Pine variable named set_trade_history_streamable becomes a data member that hides it. New tests/test_cr150_constructor_names.py: each of the three UDT names transpiles and the generated C++ compiles against the paired engine headers (and declares the escaped pf_safe_ name), the policy constructor compiles plain and with the colliding variable, the constructor qualifies its host calls, the four reservations are exported through CPP_RESERVED, and an unrelated type keeps its name. Tests only; on the current head the compile cases fail by ambiguity and the other checks by the missing reservations and the unqualified calls.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…qualify the generated constructor's host calls (REV-CX r4 F2 and the reserved-names P1)

1. report, report_sink and report_sequence are engine namespaces in the 1.7.0 headers; a Pine type or variable with one of those names collided with them in the generated file (`using namespace pineforge;`). They join CPP_EMITTER_NAMES (helpers.py), so _safe_name escapes them; CPP_RESERVED, the existing public export, now lists them, and docs/PUBLIC_CONTRACT.md documents the reservation. They are namespaces, not host members, so they do not belong to the generated HOST_MEMBER_NAMES.
2. set_trade_history_streamable is a public BacktestEngine method the emitter spells in the generated constructor; host_members.py was not regenerated for it (161 names now), so a Pine variable of that name became a data member hiding the call. Added by hand because scripts/gen_host_members.py needs clang; the lab must confirm the set with tests/test_host_member_names.py (test_committed_host_members_are_the_derived_set, test_generated_code_reads_no_unreserved_host_member).
3. The generated constructor now calls this->set_trade_history_streamable(true) and this->configure_pine_strategy(cfg), like its adapter and cap hooks. The host base is not a template, so the unqualified calls are valid C++ today; the qualification is the robust form and is paired with raw-output compile tests. tests/_legacy_cpp.py and tests/test_coded_run_stops.py accept the qualified statements; no expected trading output changed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…from the receipt's reserved host-name list (CR-150)

Reserving set_trade_history_streamable (6f4f186) adds that name to the host-member list that every generated file embeds in its checked-settings receipt. Six whole-C++ identity checks that compare against references pinned before the name was reserved (four test_diagnostic_notes interface rows, test_e2e_session_history, test_codegen_ta_dynamic_length) then differed by exactly that list element and nothing else. without_trade_history_policy already normalises the other two CR-150 additions; it now also removes the reserved-name element. The raw output is covered by test_cr150_constructor_names.py and test_host_member_names.py. Lab: those six passed on the pairing before the fix commit and failed after it; the run on this commit is the proof.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…rt, a table of every name the emitter exports (red first), and the pure-Python escape assertion before the compile call

test_report_namespace_type_transpiles_and_compiles now asserts the escaped struct before compile_cpp, so a spelling regression fails on hosts without a compiler (argon F-2, Opus P2-4). New rows: a Pine variable named PINEFORGE_HAS_TRADE_HISTORY_POLICY_V1; a type and a function named strategy_reads_trade_history (the export stays unescaped and defined once); and test_emitted_external_name_is_reserved[kind-name], one row per PINEFORGE_HAS_*_V1 guard and per extern "C" export derived from the emitter's own source text (it raises, never skips, when its scan finds nothing). On dfac42a the policy macro and the export rows are red as argon found them; the inventory also lists 13 further emitted names that no reservation set contains, handled in the next commit series.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…gy_reads_trade_history export, and document both (CR-150, argon F-1 and Opus P2-1, P2-2)

A Pine variable named PINEFORGE_HAS_TRADE_HISTORY_POLICY_V1 emitted `int PINEFORGE_HAS_TRADE_HISTORY_POLICY_V1 = 0;`, which the 1.7.0 header's macro turns into `int 1 = 0;`; a type or function named strategy_reads_trade_history collided with the generated extern "C" export. The macro joins CPP_STANDARD_MACROS, the export joins CPP_EMITTER_NAMES, and docs/PUBLIC_CONTRACT.md lists both with their pf_safe_ script spellings and the unchanged export spelling. host_members.py is not touched. Neither set is serialized into a receipt, so no pinned whole-C++ comparison moves.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… names, a Pine function named like the policy setter, and the order-shapes host_reads of readers and non-readers (red first)

test_report_namespace_type_transpiles_and_compiles gains the scopes the emitter names but did not reserve (source, native_calendar, RunFailureCode, pine_color, QtyType, CommissionType, XLoc, YLoc, RunFailureInfo, normalize_timezone_for_posix), each asserting the escaped struct before the compile call. test_script_function_named_policy_setter_is_escaped covers a Pine function set_trade_history_streamable (before the reservation it was emitted under its own name and `this->set_trade_history_streamable(true)` bound to it, silently skipping the policy). test_host_reads_names_policy_setter_for_non_readers_only (tests/test_order_shapes.py) pins the order-shapes receipt: the setter is in host_reads for a script that does not read trade history and not for one that does. The comment in tests/_legacy_cpp.py now names the order-shapes receipt's host_reads, which is what the normaliser removes. The existing table test_emitted_external_name_is_reserved is unchanged; it is red for the 13 further emitted names the previous commit's inventory found (PINEFORGE_HAS_SYMBOL_SECURITY_EVAL_V1 and 12 extern "C" exports).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…he emitter names, and document them (CR-150 class sweep, Opus P2-3)

PINEFORGE_HAS_SYMBOL_SECURITY_EVAL_V1 is a real macro collision (the 1.7.0 header defines it to 1, so a Pine variable of that name is macro-expanded) and joins CPP_STANDARD_MACROS; the twelve extern "C" exports the emitter defines (strategy_create, strategy_create_checked, run_backtest, run_backtest_full, strategy_free, report_free, strategy_set_input, strategy_set_override, strategy_set_magnifier_volume_weighted, strategy_set_input_checked, strategy_set_override_checked, run_backtest_full_checked) join CPP_EMITTER_NAMES for the emitted-name invariant (no collision reproduced for them under the current ordering of the generated file). The engine scopes the emitter names unqualified or via `using namespace pineforge;` join CPP_EMITTER_NAMES: source, native_calendar, RunFailureCode, pine_color, QtyType, CommissionType, XLoc, YLoc, RunFailureInfo, normalize_timezone_for_posix, so a Pine `type source` becomes pf_safe_source instead of a global struct ambiguous with pineforge::source. docs/PUBLIC_CONTRACT.md lists all of them. host_members.py is not touched. A Pine identifier that uses one of these names is now emitted as pf_safe_<name>, which changes the generated text of such scripts only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…_safe_source; fifteen pinned comparisons compare the rename-reversed text, and a row proves the reservation is present (CR-150)

Reserving `source` (Opus P2-3) changes the generated text of every script that names a variable, parameter or function `source`, and fifteen pre-existing tests pinned the old spelling (test_array_terminal_returns 10 rows, test_array_checked_access, test_map_param_methods, test_math_sign_na_codegen 2, test_map_call_diagnostics' sha256 pin c955f34b...). tests/_legacy_cpp.py::without_source_rename reverses pf_safe_source to source for whole identifiers in code regions only (never inside strings or comments, never a longer name such as pf_safe_source_2); the fifteen tests apply it, so every expected literal and the old digest stay unchanged and any other drift still fails. tests/test_legacy_cpp.py pins the helper's boundary rule. test_source_variable_is_escaped_in_generated_code goes red if the reservation is lost, so the helper cannot hide that. The policy-macro row's assertion is corrected: the raw macro name may be spelled only in the generated `#if defined(...)` guard.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…r its reserved spelling (no chart global: compile; with a chart global: its own value), the rename helper must refuse a partial rename, and the clone of a reserved UDT must compile (red first; CR-150, Opus and argon NO-GO F1, F2, argon F-2)

test_e2e_security_payload_calls.py: the h() payload with `source = high - low`, `nz(source) - nz(g[1])` and `float g = close - open` (nz(g[1]) forces chart mode) must compile with no chart-level `source` and, with a chart-level `source = open`, must use its own local, checked against the same script with the variable renamed. test_legacy_cpp.py: a partial rename (declaration pf_safe_source, a use `source`) must make the reversal helper raise, a complete rename must reverse cleanly, a text without pf_safe_source is returned unchanged. test_cr150_constructor_names.py: `type source`, `type report` and `var source s = source.new(close)` in a two-call-site UDF must transpile and compile (argon F-2). Expected on 06ce40b: the security rows fail (undeclared pf_safe_source / wrong binding), the partial-rename row fails (no guard yet).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…elling, spell a reserved UDT's clone type safely, and make the rename-reversal helper refuse a partial rename (CR-150, Opus and argon NO-GO F1, F2, argon F-2)

security.py: the request.security post-pass rewrote helper-bound locals by the RAW Pine name (the subscripted-series, current-series and scalar substitutions) while the expression visitor spells the local through _safe_name; with `source` reserved the declaration is pf_safe_source and the raw pattern never matched, so the payload either failed to compile (no chart global) or bound the chart-level `source` (wrong value). The three substitutions now match the same _safe_name spelling as the mutable-global branch two lines above. base.py: the clone declaration of a reserved UDT used the raw type name; it uses _safe_name. tests/_legacy_cpp.py: without_source_rename calls assert_source_rename_complete first, which fails when, outside strings and comments, the raw text holds both a pf_safe_source identifier and a bare `source` identifier that the emitter does not itself spell (pineforge::source:: qualifications and the include path), so a partial rename can no longer be reversed into the old pinned text. The class sweep over codegen/*.py raw-name rewrites found no other site of this class (listed in the lane report).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@luisleo526
luisleo526 marked this pull request as ready for review October 24, 2026 03:42
@luisleo526
luisleo526 merged commit 89b48cb into main Oct 24, 2026
19 of 20 checks passed
luisleo526 added a commit that referenced this pull request Oct 24, 2026
Adds "## 1.7.0 — 2026-10-25" to CHANGELOG.md per the file's release note policy
(shape of the 1.6.1 section): compatibility and migration (exact pair with engine
v1.7.0, regenerate C++ and relink against the engine v1.7.0 headers and
libpineforge.a) and the three pull requests merged after v1.6.3:
#213 (the user-observable change: the strategy_reads_trade_history export, the
guarded set_trade_history_streamable constructor call for scripts that do not read
closed-trade rows, the qualified constructor host calls, and the larger set of
reserved names with their pf_safe_ escape, from the diff of 89b48cb and
docs/PUBLIC_CONTRACT.md), and #242 and #243 as repository/CI only. No backfill of
1.6.2 or 1.6.3 (follow-up card), no claim about the engine beyond its own notes,
VERSION untouched (release.yml writes it). CHANGELOG.md only.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
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