From fc4ede7686f2ead370e8ea90926211d0e93f5a2a Mon Sep 17 00:00:00 2001 From: luisleo526 Date: Thu, 8 Oct 2026 08:39:16 +0800 Subject: [PATCH 1/3] docs: prepare scoped 1.4.0 codegen release notes Consolidate the Unreleased entries from 2d157ec, 4c3eb7f, 6db794f, 1e6eb83 and bfc4ddce; record CI/reference requests d6388d5 and 98df3bf. Replace the request-local Generated C++/manifest identity wording from 6db794f with release-scoped compatibility and inherited limitations. Update current-release prose from cb5eb94 and b654c2a to the planned 1.4.0 pair without changing historical measurements or VERSION. Render registry grades from the prepared export; supervisor rebinds landed facts and records the docs gate before publication. Co-Authored-By: Codex --- CHANGELOG.md | 134 ++++++++++++++++++++++++++++++++---- README.md | 71 +++++++++++++------ docs/PUBLIC_CONTRACT.md | 62 ++++++++++++++--- docs/pine-cap-activation.md | 17 +++-- 4 files changed, 233 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2446603..64ab481 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,10 +20,54 @@ supported as exact pairs; on the 0.x line they are independent. See the the release commit. A prerelease note describes changes since the preceding prerelease or stable tag; the final stable note consolidates the series. -## Unreleased +## 1.4.0 — Unreleased + +A minor release of the exact engine/codegen pair, prepared from codegen +`bfc4ddce` and engine `b3192bfc`. It consolidates the seven requests landed +since `v1.3.0`: [#183](https://github.com/pineforge-4pass/pineforge-codegen-oss/pull/183) +(emission ordering), [#184](https://github.com/pineforge-4pass/pineforge-codegen-oss/pull/184) +(coded run stops), [#185](https://github.com/pineforge-4pass/pineforge-codegen-oss/pull/185) +(input manifests), [#186](https://github.com/pineforge-4pass/pineforge-codegen-oss/pull/186) +(collection history and binding), [#188](https://github.com/pineforge-4pass/pineforge-codegen-oss/pull/188) +(CI history), [#189](https://github.com/pineforge-4pass/pineforge-codegen-oss/pull/189) +(reference tests), and [#190](https://github.com/pineforge-4pass/pineforge-codegen-oss/pull/190) +(emitted-string regressions and edge cases). Recorded-output support from +engine request #356 is excluded. This planned release has not been tagged; +its candidate `VERSION` files still read `1.3.0` until the release workflows +set them. -- Generated C++ no longer depends on memory layout: requested-context history - resets and TA variant position ties preserve their traversal order. Refusal +### Compatibility and migration + +- Codegen 1.4.0 supports only engine `v1.4.0`. Regenerate every strategy's C++ + and relink with that release's generated headers and `libpineforge.a`. + The pair retains C ABI version 4, settings/capability API version 1 and the + `engine_script_run_v19` epoch; equal version numbers for those interfaces + do not make mixed engine/codegen releases supported. +- Emitted C++ changes, including common run-stop shims and collection history + wrappers. Do not reuse a 1.3.0 strategy library or assume source identity. + The public Python call signatures and override keys stay the same, but + `inputs` values and the Python/Pyodide success envelopes change as described + below. These are not blanket byte-identity promises for emission or envelopes. +- Use `inputs[].supported` when deciding which inputs to offer or optimize. + Do not infer support from empty `options`; a supported empty dropdown and + an unsupported string input can both have `options: []`. +- The paired engine adds `strategy_get_last_error_code` and + `strategy_get_last_error_args` beside the existing English error getter, + under `PINEFORGE_HAS_RUN_FAILURE_CODES_V1`. Its run harness exposes `code` + and typed `args` beside `error` on failures. Check the failure channel even + when the English message is empty; do not accept a stopped run's partial + output as success or parse the English to recover a code. These run failures + are distinct from transpile-time `CompileError` diagnostics. +- The paired engine certifies scalar input/override types in fingerprint + provenance; unresolved values remain explicit rather than being treated as + certified native values. This is an engine report change, not another field + added to the transpile-time input manifest. See the paired engine's release + notes for report resolution reasons and counts. + +### Emission and run failures + +- Requested-context history resets and TA variant position ties no longer + use address or set order; they preserve traversal order. Refusal diagnostics for conflicting scalar history and map locals are hash-seed independent. - Generated run stops use stable engine failure codes when the paired engine @@ -41,48 +85,108 @@ supported as exact pairs; on the 0.x line they are independent. See the names; substring arguments retain source order without copying the source text. - Generated run wrappers preserve the inner failure code, and latched setter failures and checked strategy construction retain their failure identity. + +### Python and Pyodide input metadata + - The transpile-time input manifest (`transpile_full()["inputs"]` and the JSON envelope of the Pyodide package) gains `supported`, the checked-settings receipt's flag, on every input, and reads its defaults, choices and bounds - from the descriptor that generates the receipt, so the two agree for every - input of the public corpus and the repository's fixtures. Generated C++, - `strategyParams` and `requests` are unchanged and no key is renamed or - removed, but these values of existing keys change: + from the descriptor that generates the receipt, with the retained limitations + below. No manifest key is renamed or removed, but these values of existing + keys change: - Source inputs (`input.source`, a plain `input(close)`): `default` is the series name, where it was `null`, and `options` lists the nine native sources, where it was absent. - Enum inputs: `options` lists the `Enum.member` choices, where it was - absent. + absent; `default` keeps its `Enum.member` spelling. The compiled receipt + encodes that default as its integer member value. - String inputs: a built-in constant publishes its runtime value (`alert.freq_all` is `"all"`, `currency.USD` is `"USD"`; the manifest held the Pine names), a named string constant its value, and the choices of `input.timeframe` and `input.session` are listed. What the receipt cannot - represent (`size.small`, `position.top_right`, `na`) is `default: ""`, - `options: []` and `supported: false`. - - Typed `int`, `float`, `price`, `time` and `bool` inputs: a signed or + represent (`size.small`, `position.top_right`, `na`) has `supported: false` + and `options: []`; an unavailable default is `""`, but a representable + default survives an unsupported choice. String decoding uses the emitter + escape grammar, keeps literal Unicode and escaped backslashes, and stops + at the first NUL like the receipt's `std::string(const char*)` constructor. + NUL is emitted as a fixed three-digit octal escape even before digits; + this is consistency with current native truncation, not lossless NUL storage. + - Typed `int`, `float`, `price` and `time` inputs: a signed or named-constant `default` is a number, where it was `null`; `min`, `max` and `step` appear for signed and named-constant bounds (`minval=-80` was left - out); an `options=[...]` dropdown lists its numbers. + out); an `options=[...]` dropdown lists its numbers. A literal or inlined + `bool` default publishes a boolean, not a number. - A plain `input(-5)` or `input(-2.5)` is typed `int` or `float` with its - number as `default`, where it was `string` with `null`. + number as `default`, where it was `string` with `null`. The compiled + getter and checked receipt keep their existing float type for plain + numeric inputs; this manifest correction does not change that lowering. Values the receipt computes at run time (`LEN * 2`, `not true`, - `timestamp(year, month, ...)`, a color) stay unpublished, as before. + `timestamp(year, month, ...)`) stay unpublished: the default remains + `None`/JSON `null`, an unfoldable bound is omitted, and an unsupported + numeric options expression can leave an empty list. An `input.color` + retains its Pine spelling as a string default, not the receipt's packed + integer; other nonliteral plain `input()` calls remain `string` with no + published default (the source case above is the exception). A folded + `timestamp("...")` is a publishable literal. These gaps + are explicit in `tests/test_input_metadata_receipt.py::KNOWN_LIMITS`, not a + claim of arbitrary expression evaluation or language soundness. - Documents the timeframe spellings of two surfaces, both stable: a capability receipt records a request's timeframe as the script wrote it (`"D"`), and request discovery and feed keys use the engine's spelling (`"1D"`). + +### Collection lowering and refusals + - Bind keyword arguments of the matrix methods and the checked array methods to their own parameter slot, with the omitted optional parameters taking their defaults in place (`matrix.sort(m, order = order.descending)`, `m.submatrix(to_column = 1)`, `a.fill(7.0, index_to = 1)` no longer drop or shift a keyword), and refuse a keyword that names no parameter, or a parameter given twice, with the existing argument diagnostics. A defaulted `submatrix` evaluates its receiver once, an index-only `add_row` / `add_col` takes any numeric index, and a bound `array.concat` result (`c = a.concat(b)`, `c := array.concat(a, b)`) keeps the unsupported-function refusal. An array history that reaches a user function's or method's parameter keeps its earlier copy lowering and the earlier refusal of a callee that checks it with `na()`; a bound missing-history matrix reports the recorded na-ID runtime error. - Compile matrix/matrix and matrix/scalar `matrix.sum` overloads with matrix-valued results, including helpers and history. Support scalar `matrix.diff`/`matrix.mult` and omitted optional arguments to matrix row/column insertion, submatrix, sort and array fill; unsupported matrix/vector multiplication and bound `array.concat` results report an existing unsupported-function diagnostic instead of failing C++ compilation. - Preserve na array IDs when a history offset has no value, including aliases and `na()`-only bindings; array methods on those IDs report the existing runtime error instead of crashing. Bounds-check legacy collection-element reads and writes to prevent unchecked access. + +The inherited follow-ups remain: keywords on UDT matrix fields, `matrix.new` +and array methods outside the checked table; `matrix.fill` range handling +(both spellings still fill the whole matrix); matrix diff/mult dimension +checks; and nullable history across callable/iteration shapes not covered by +the documented lowering. This release does not claim those cases resolved. + +### Emitted-string edge cases and maintenance + - Return the prefix or ticker of the supplied symbol in `syminfo.prefix(symbol)` and `syminfo.ticker(symbol)` instead of always reading the chart symbol; the - variable forms stay unchanged. + variable forms, no-argument calls and direct chart-ID calls keep their + chart lowering. Supplied-symbol calls split at the first colon; a prefix + is empty without a colon or with no ticker after it, and a ticker without + a colon is the full string. Non-string arguments still compile and yield + an empty string, including the retained string-`na` typing gap. - Refuse malformed library versions with the existing import diagnostic instead of allowing them to raise outside the JSON gate, including non-ASCII decimal digits and ASCII versions exceeding Python's integer-conversion limit. - Reserve generated receipt exports and version macros against script names. - Escape embedded NUL values in generated C++ string literals, and strengthen emitted-string regression tests with explicit emitter reach and round trips. +- CI fetches full branch history for historical reference tests (#188); two + whole-C++ comparisons use the accepted immutable scaffold (#189). These + changes affect verification only, not the compiler or execution semantics. + +The hardening wording remains scoped to the previously documented emitted-string +validation and escaping; these edge-case repairs and regression checks are not +a new arbitrary-source security guarantee. No exploit payloads are included. + +### Recorded validation and grades + +The landed #190 candidate recorded 9,254 engine-enabled pytest passes with +12 audited skips, 2,164 focused passes each under GCC and Clang without skips, +314 public corpus compilation passes, and 279 JSON/Pyodide fixtures. Those +are the request's retained native measurements, not a new native suite run +for this documentation change. The release-pair baseline records +excellent: 7,983; +strong: 6; +graded: 7,989 probes, +with 0 below strong +and 0 engine errors. +These are recorded grading outcomes, not a statement that no internal attempt +ever failed. Baseline `pineforge-parity-baseline-20261007-engine-b3192bfc` +measures engine `b3192bfc` +and codegen `bfc4ddce`; +these quantities render from the prepared release facts, not numeric overrides. ## 1.3.0 — 2026-10-06 diff --git a/README.md b/README.md index 1bcf1ec..42e1d75 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,14 @@ A pure-Python library that turns a PineScript v6 strategy into a complete C++ source file you can compile against the [`pineforge-engine`](https://github.com/pineforge-4pass/pineforge-engine) runtime. -**Measured 2026-10-06** on main engine `7a1f01c0` with codegen-oss `3e50082f` (baseline `pineforge-parity-baseline-20261006-engine-7a1f01c0`, snapshot `e7921e24`): 7,982 of 7,989 TradingView probes -graded excellent and 7 strong, with 0 below strong; 17 more probes are held out as TradingView-side anomalies. +**Measured 2026-10-07** on main engine `b3192bfc` with codegen-oss `bfc4ddce` (baseline `pineforge-parity-baseline-20261007-engine-b3192bfc`, snapshot `28ba8dbd`): 7,983 of 7,989 TradingView probes +graded excellent and 6 strong, with 0 below strong; 17 more probes are held out as TradingView-side anomalies. A probe is a strategy exported from TradingView with its trade list and replayed trade for trade on the same bars. -Release **1.3.0 grades 7,982 excellent / 7 strong until the next release**, on 7,989 probes (baseline `pineforge-parity-baseline-20261006-engine-7a1f01c0`, 2026-10-06). A main scoreboard advance does not change release results. +Planned release **1.4.0 grades 7,983 excellent / 6 strong**, on 7,989 probes (baseline `pineforge-parity-baseline-20261007-engine-b3192bfc`, 2026-10-07). These are the prepared pair's recorded grading outcomes, not a published tag or a promise about arbitrary scripts. A main scoreboard advance does not change release results. + +Release 1.3.0 recorded 7,982 excellent / 7 strong on 7,989 probes; its historical result remains scoped to that pair. The quantities above render from the public [facts tokens](https://github.com/pineforge-4pass/pineforge-release/blob/main/facts/facts.json). Maintain them with `lab facts render --repo . --facts `; `lab facts check` with the same inputs reports drift. Grades are registry-derived; the authored-script and closed-trade inventory is explicitly sourced to a historical public README for the identical population, not to registry row or slug totals. @@ -40,7 +42,7 @@ for the changes in each release from 1.0.0 on and the release-note policy. ## Releases and this README This README ships with each release as its package description on PyPI (`pineforge-codegen`); releases from 0.7.0 on are also on npm as -`@pineforge/codegen-pyodide`. It describes 1.3.0 (2026-10-06) and what changed -since 0.10.4. The +`@pineforge/codegen-pyodide`. It describes the planned 1.4.0 pair (**Unreleased**) +and what changed since 0.10.4. The candidate is codegen `bfc4ddce` with engine +`b3192bfc`, excluding engine request #356; both candidate `VERSION` files still +read `1.3.0` until the release workflows set them. Historical measurements +below retain the versions on which they were run. The [PyPI release history](https://pypi.org/project/pineforge-codegen/#history) lists every release; the [changelog](https://github.com/pineforge-4pass/pineforge-codegen-oss/blob/main/CHANGELOG.md) @@ -59,6 +64,21 @@ covers 1.0.0 on and links the notes of earlier releases. A source install reports the version in `VERSION`, which the release workflow sets when it tags a release. +### Upgrading from 1.3.0 + +After the 1.4.0 pair is published, regenerate C++ and relink against engine +`v1.4.0`'s generated headers and runtime, even though C ABI version 4 and the +script ABI epoch are retained. Emission and input manifests change; neither +generated C++ nor Python/Pyodide envelopes are promised byte-identical. Use +`inputs[].supported` for UI/optimization eligibility and account for corrected +source/enum choices, string values, signed numeric bounds and plain-input types. + +The paired engine adds coded run failures and typed fingerprint provenance. +Its run harness returns failure `code` and typed `args` beside `error`; an +empty message still means failure. Transpile-time diagnostics remain a separate +Python/JSON surface. Read the [1.4.0 changelog](CHANGELOG.md#140--unreleased) +for migration details and inherited collection/metadata limitations. + ### Upgrading from 0.10.4 0.10.4 (2026-09-06) was the last 0.x release. Its README is at the @@ -90,8 +110,9 @@ It does **not** own execution semantics. Order lifecycle, bracket legs, fill-price and slippage rules, `process_orders_on_close` / `calc_on_order_fills`, margin revival and trail/stop behaviour — everything TradingView parity depends on at run time — live in the engine's source-adapter runtime -([`src/source/`](https://github.com/pineforge-4pass/pineforge-engine/tree/v1.3.0/src/source) -in engine `v1.3.0`), which maps them onto the engine's Pine-agnostic kernel. See +([`src/source/`](https://github.com/pineforge-4pass/pineforge-engine/tree/v1.4.0/src/source) +for the planned engine `v1.4.0` pair), which maps them onto the engine's Pine-agnostic kernel. The tag link becomes available when that release is +published. See the engine's [architecture notes](https://github.com/pineforge-4pass/pineforge-engine#architecture-kernel-vs-parity). --- @@ -186,7 +207,7 @@ It returns `{"cpp": str, "inputs": list[dict], "strategyParams": dict, returns the first three keys; `requests` is new in 1.1.0). `inputs` is the input manifest; its `title` is the actual override key, and an `input.symbol` entry also has `"kind": "symbol"` (since 1.1.0). `diagnostics` contains nonfatal -warnings. Each input also has `supported`, the checked-settings receipt's +warnings. Since 1.4.0, each input also has `supported`, the checked-settings receipt's flag, so a consumer can hide the inputs the compiled strategy cannot honour and keep them out of parameter sweeps and optimization before running anything. Defaults, bounds and choices are the receipt's values where the @@ -196,11 +217,19 @@ lists its choices, and a typed number input publishes a signed or named-constant default and bound and the numbers of an `options=[...]` dropdown. An unrepresentable string default is `""`, and every unsupported string input has `options: []` (a representable default is kept), so key on `supported`, not on -`options`. A value the receipt computes at run time (arithmetic over -constants, a call, `not true`, a color) is not published: `default` stays -`None` and `min`, `max` and `options` are left out (the -[contract](docs/PUBLIC_CONTRACT.md#input-manifest-and-override-keys) lists -the shapes). The PyPI and Pyodide manifests carry the same fields; no key is +`options`. Plain `input(-5)` and `input(-2.5)` now publish `int` and `float` +manifest types and numeric defaults; their compiled getters and checked +receipts retain the existing float type. A plain `input(close)` publishes +source choices but retains its `string` manifest type. String metadata +follows native C-string construction: an +embedded NUL truncates the published default/choice at the first NUL; it is +not a lossless-string promise. Literal Unicode and escaped backslashes are +retained. Run-time arithmetic, `not true` and calendar-part `timestamp` values +still have no published default or unfoldable bounds; a folded +`timestamp("...")` is a literal. Numeric computed choices can leave an empty +list, and colors retain their Pine spelling, not their packed native value. +The [contract](docs/PUBLIC_CONTRACT.md#input-manifest-and-override-keys) lists +these retained limits. The PyPI and Pyodide manifests carry the same fields; no key is renamed or removed, and the changelog lists the values that changed. `requests` lists the other symbols' feeds the script reads (see [List the other symbols a script requests](#list-the-other-symbols-a-script-requests)). @@ -473,6 +502,7 @@ Generated C++ compiles only against the engine it was generated for: | 1.1.0 (PyPI, 2026-10-04) | `v1.1.0` | The pair the `pineforge-release` image `1.1.0` ships. Its C++ defines the checked settings functions that engine `v1.1.0` adds to ``; regenerate and relink. | | 1.2.0 (PyPI, 2026-10-05) | `v1.2.0` | The pair the `pineforge-release` image `1.2.0` ships. Its C++ defines the compiled execution capability functions that engine `v1.2.0` adds to ``; regenerate and relink. | | 1.3.0 (PyPI, 2026-10-06) | `v1.3.0` | The pair the `pineforge-release` image `1.3.0` ships. Its C++ adds the confirmed-bar receipt, which engine `v1.3.0`'s live runner reads, and the order-shapes receipt; engine `v1.3.0`'s `` is `v1.2.0`'s. Regenerate and relink. | +| 1.4.0 (planned, Unreleased) | `v1.4.0` | Prepared from codegen `bfc4ddce` and engine `b3192bfc`. Adds paired coded failures, corrected input manifests and collection lowering. C ABI 4 does not authorize a mixed pair. Regenerate and relink after publication. | | Later `X.Y.Z` releases | `vX.Y.Z` of the same version | See below. | On the 0.x line the engine and codegen versions are independent, and the @@ -498,8 +528,8 @@ Pine as `strategy.pine`, write `strategy.generated.cpp` with the [file example](#transpile-a-file-to-a-cpp) above, then from the same directory: ```bash -# 1.3.0 pairs with engine v1.3.0. For 0.10.4 use --branch v0.13.1. -git clone --branch v1.3.0 https://github.com/pineforge-4pass/pineforge-engine.git +# After publication: 1.4.0 pairs with engine v1.4.0. For 0.10.4 use v0.13.1. +git clone --branch v1.4.0 https://github.com/pineforge-4pass/pineforge-engine.git cd pineforge-engine cp ../strategy.generated.cpp tutorial/macd/generated.cpp # the tutorial's strategy slot bash tutorial/run.sh # needs cmake, a C++17 compiler and python3 @@ -508,7 +538,8 @@ bash tutorial/run.sh # needs cmake, a C++17 compiler and python3 `run.sh` configures CMake once, builds `libpineforge.a` and `tutorial/macd/strategy.so`, and runs `tutorial/run.py`, which loads the `.so`, feeds it the bars and reads back the closed trades. For the quick-start SMA -cross, 1.3.0 with engine `v1.3.0` prints: +cross, the following output was measured on 1.3.0 with engine `v1.3.0`; it +has not been re-measured for this 1.4.0 documentation change: ``` MACD(12,26,9) on BTCUSDT 15m — 672 bars, 2026-04-29 18:15 → 2026-05-06 18:00 UTC @@ -612,7 +643,7 @@ quote. The commercial-license store (coming soon) will take orders online. ## Explicit Pine execution attachment -This section describes 1.3.0 and engine `v1.3.0`. The Pine execution adapter is +This section describes the planned 1.4.0 and engine `v1.4.0` pair. The Pine execution adapter is the engine's full Pine execution runtime (`PineExecutionAdapter` and `PineStrategyHost` in the engine's `src/source/`): order lifecycle, bracket legs, fill-price and slippage rules, POOC / `calc_on_order_fills`, margin @@ -635,9 +666,9 @@ Regenerate old generated C++ before using a new engine for Pine execution. C++ generated before the source-layer cut ([#129](https://github.com/pineforge-4pass/pineforge-codegen-oss/pull/129)), 0.10.4's included, derives from `BacktestEngine` and does not compile against -engine `v1.3.0`; old cap-only C++ does not attach the priority rule, and +engine `v1.4.0`; old cap-only C++ does not attach the priority rule, and metadata cannot silently restore it. Rebuild all modules against the -new matching C++ layout (`engine_script_run_v19` in engine `v1.3.0`); old +new matching C++ layout (`engine_script_run_v19` in engine `v1.4.0`); old fingerprint versions are not comparable. The extraction preserves Pine policy under explicit attachment; it does not implement the generic native child-activation scheduler or prove campaign neutrality. Compile-only corpus diff --git a/docs/PUBLIC_CONTRACT.md b/docs/PUBLIC_CONTRACT.md index e86a2d9..63576b3 100644 --- a/docs/PUBLIC_CONTRACT.md +++ b/docs/PUBLIC_CONTRACT.md @@ -1,5 +1,10 @@ # Public contract for 1.0 +The supported Python/JSON contract began in 1.0.0; the sections below identify +later additions. This revision includes the planned 1.4.0 pair (codegen +`bfc4ddce`, engine `b3192bfc`, **Unreleased**), not an already published tag. +The candidate version files still read `1.3.0` until release dispatch. + ## Optional compiled execution capabilities Availability: **since 1.2.0**, with engine `v1.2.0`'s capability extension and @@ -115,6 +120,31 @@ its override are raw: fold them the same way. To match a receipt's request to a feed, fold the receipt's literal timeframe with that rule (folding twice changes nothing); never map a feed key back to `"D"`. +## Optional coded run failures + +Availability: **planned 1.4.0**, with engine `v1.4.0`'s +`PINEFORGE_HAS_RUN_FAILURE_CODES_V1` feature. The feature is optional in emitted +source; that fallback is not an exception to exact release pairing. + +Generated no-data/other-symbol, collection, UDT, unsupported-request and +checked-string stops carry compile-time provenance through coded helpers. +A script's `runtime.error` always selects `strategy_runtime_error`; neither +its message nor an English message copied from another failure selects a +code. Named `message` arguments are passed, and an omitted message stays empty. +`str.tonumber` evaluates its argument outside the numeric parser's narrow +catches so a run stop propagates; only numeric parsing failures become `na`. +The generated run wrappers retain inner codes. A legacy setter's first failure +latches its English and `setting_rejected` identity for subsequent begins. + +The paired engine provides `strategy_get_last_error_code` and +`strategy_get_last_error_args` beside `strategy_get_last_error`; its run harness +adds `code` and typed `args` beside failure `error`. An empty English message +does not make the run successful or authorize use of its partial results. +Without the feature, generated shims retain the legacy exceptions and English. +This is a runtime failure channel, distinct from the `CompileError` and +`diagnostics` contract of transpilation. Regenerate and relink for the new pair; +C ABI version 4 alone does not authorize reusing a 1.3.0 strategy library. + ## Optional generated settings extension From 1.1.0 on, C++ compiled against an engine providing @@ -285,7 +315,8 @@ an `input.symbol` entry also has `kind` equal to `"symbol"`. An optional field is omitted when its argument is absent or cannot be reduced to the supported literal form. -`supported` and the values below are those of the checked-settings receipt (what +From 1.4.0, `supported` and the values below come from the checked-settings +descriptor, subject to the explicit retained limits below (what the compiled strategy's `strategy_get_effective_settings` reports): the manifest is read from the descriptor that generates the receipt, and PyPI's `transpile_full()` and the npm package's Pyodide/glue success envelope carry @@ -304,7 +335,13 @@ them identically. (`alert.freq_all` is `"all"`, `currency.USD` `"USD"`, `format.price` `"price"`, `order.ascending` `"ascending"`, `session.regular` `"regular"`) and a named string constant its value. An unrepresentable default is `""`; a - representable default is kept when only a choice is unsupported. + representable default is kept when only a choice is unsupported. The + descriptor decodes only the emitter's C++ string-literal escape grammar, + preserves literal Unicode and escaped backslashes, and stops at the first + embedded NUL, matching native `std::string(const char*)` construction. The + emitter spells NUL as fixed three-digit octal `\000`, including before + digits. This is current manifest/receipt consistency, not lossless storage + of a string containing NUL. - A source input (`input.source(hl2)`, a plain `input(close)`) publishes its series as `default` and the nine native sources (`close`, `high`, `hl2`, `hlc3`, `hlcc4`, `low`, `ohlc4`, `open`, `volume`) as `options`; a plain @@ -312,21 +349,26 @@ them identically. - An enum input publishes its `Enum.member` choices in declaration order as `options` and keeps its `Enum.member` `default`, which the receipt encodes as the member's index (map it through the receipt's `option_values`). -- A typed number or bool input (`input.int`, `.float`, `.price`, `.time`, - `.bool`) publishes `default`, `min`, `max`, `step` and the choices of an - `options=[...]` dropdown (a list of numbers) as numbers where the receipt's - value is a literal: a signed number (`-2.5`), a named constant (`LEN = 14`, +- A typed numeric input (`input.int`, `.float`, `.price`, `.time`) publishes + numeric `default`, `min`, `max`, `step` and the numeric choices of an + `options=[...]` dropdown where the receipt's value is a literal: a signed number (`-2.5`), a named constant (`LEN = 14`, `minval=-RATIO`) or `timestamp("2024-01-02T00:00:00")` of a string literal. A plain `input(-5)` or `input(-2.5)` is typed `int` or `float` by its literal, as `input(5)` is; the compiled getter reads it as a double, so the receipt's - type for it is `float`. + type for it is `float`. An `input.bool` default is a boolean when the + descriptor lowers it to literal `true` or `false`, including an inlined + constant; `not true` remains a run-time expression. - Not published, because the receipt computes it at run time: arithmetic over constants (`LEN * 2`), a call (`math.pow(2, 3)`), `timestamp(year, month, ...)` (it reads the symbol's time zone) and `not true`. Such a `default` is - `None` and such a `min`, `max` or `options` is omitted. An `input.color`'s + `None` and such a `min` or `max` is omitted. Numeric choices containing + an unfoldable expression can publish `options: []`, not its computed + values. These gaps are pinned in `tests/test_input_metadata_receipt.py`'s + `KNOWN_LIMITS`. An `input.color`'s `default` is its Pine spelling (`"color.red"`) and its `type` `string`, where - the receipt holds the packed integer, and a plain `input(...)` whose default - is not a literal is typed `string` with a `None` default. + the receipt holds the packed integer. Apart from the source-input case + above, a plain `input(...)` whose default is not a literal is typed + `string` with a `None` default. No key is renamed or removed and the override keys are unchanged; the values above are new or corrected values of existing keys, listed in the changelog. diff --git a/docs/pine-cap-activation.md b/docs/pine-cap-activation.md index ed953fd..a151f32 100644 --- a/docs/pine-cap-activation.md +++ b/docs/pine-cap-activation.md @@ -4,7 +4,9 @@ This note came with the explicit cap attachment ([#127](https://github.com/pineforge-4pass/pineforge-codegen-oss/pull/127)). It now also covers the execution-adapter attachment that followed ([#128](https://github.com/pineforge-4pass/pineforge-codegen-oss/pull/128)) and -describes codegen 1.3.0 with engine `v1.3.0`. +describes the planned codegen 1.4.0 / engine `v1.4.0` pair. The release +is **Unreleased**: candidate codegen `bfc4ddce` and engine `b3192bfc` still +have `VERSION` equal to `1.3.0`; they are not tagged 1.4.0 artifacts. Generated strategies explicitly select Pine intraday-cap compatibility in their constructor. When the engine defines @@ -20,7 +22,8 @@ The expression is evaluated only where the source executes it, and limit updates preserve the existing quota and pending obligations. Selection does not evaluate a limit or move a conditional statement into the constructor. -Engine `v1.3.0` defines both macros in `pineforge/source/pine_strategy_host.hpp`, +The 1.4.0 engine candidate, like released engine `v1.3.0`, defines both macros +in `pineforge/source/pine_strategy_host.hpp`, the header the generated C++ includes; every engine commit with that header does, since the header and the macros arrived there together (engine #253). So the adapter branch is the one that compiles today. The cap-only branch and the @@ -30,10 +33,12 @@ capability, bare native construction leaves the Pine cap unselected. Matching runtime headers and library are required; this source bridge does not make stale compiled C++ objects compatible across internal ABI versions. -Engine `v1.3.0`, like `v1.0.0`, `v1.0.1`, `v1.1.0` and `v1.2.0`, no longer has -the `script_has_strategy_close_` member that the C++ of codegen 0.10.4 and -earlier assigns, so that C++ does not compile there; regenerate it with codegen -1.3.0. +The 1.4.0 engine candidate, like released `v1.3.0`, `v1.0.0`, `v1.0.1`, +`v1.1.0` and `v1.2.0`, no longer has the `script_has_strategy_close_` member +that the C++ of codegen 0.10.4 and earlier assigns, so that C++ does not +compile there. For the planned release, regenerate with codegen 1.4.0 and +relink with the exact paired engine release; unchanged interface numbers +do not authorize mixing releases. The cap's three compatibility options retain their existing behavior and defaults. They belong to the selected Pine component, not universal native From c203463d28526b7f872bdaf52f223ab4bd0e329f Mon Sep 17 00:00:00 2001 From: luisleo526 Date: Thu, 8 Oct 2026 09:20:40 +0800 Subject: [PATCH 2/3] docs: clarify 1.4.0 settings migration and HPO compatibility Correct the fc4ede7 sentences about computed numeric options, signed plain-input getters and fixture-parity counts; clarify the single-backslash NUL escape that the actual fc4ede7 blob already spells correctly. Replace "one override sets all of them" as the normal paired-harness contract with checked duplicate-key refusal and whole-fingerprint null. Supersede b0ed496's shared-key sentence and expand 6db794f's instruction to hide unsupported inputs with explicit checked-path refusal guidance. Expand fc4ede7 migration guidance for supported=false, checked settings, color encoding and retained fingerprint epochs; distinguish the three newly refused requests from already-failing invalid capital settings. Document the HPO 0.11.0 float-option issue and conditional upgrade path. Keep the main notes Unreleased; dating is a separate unapplied packet. Co-Authored-By: Codex --- CHANGELOG.md | 67 ++++++++++++++++++++++++++++++++++++----- README.md | 37 ++++++++++++++++------- docs/PUBLIC_CONTRACT.md | 64 ++++++++++++++++++++++++++++++--------- 3 files changed, 134 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64ab481..4e8325c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,14 +43,46 @@ set them. The pair retains C ABI version 4, settings/capability API version 1 and the `engine_script_run_v19` epoch; equal version numbers for those interfaces do not make mixed engine/codegen releases supported. + A report's fingerprint records engine/codegen versions, generated C++'s + hash and the paired engine's corrected provenance values. The retained + epoch and hash domain tags do not promise equal fingerprints, state-hash + values or trades across 1.3.0 and 1.4.0. - Emitted C++ changes, including common run-stop shims and collection history wrappers. Do not reuse a 1.3.0 strategy library or assume source identity. The public Python call signatures and override keys stay the same, but `inputs` values and the Python/Pyodide success envelopes change as described below. These are not blanket byte-identity promises for emission or envelopes. -- Use `inputs[].supported` when deciding which inputs to offer or optimize. - Do not infer support from empty `options`; a supported empty dropdown and - an unsupported string input can both have `options: []`. +- Every manifest row now carries `supported`. When it is `false`, hide the + input, exclude it from sweeps and optimization, and do not send a value: + the strategy uses its compiled default, and the paired engine refuses a + supplied value with `setting_unsupported`. `supported: true` alone does + not validate the value's type, bounds or options, or make a shared key safe. + A supported empty dropdown and an unsupported string input can both have + `options: []`; that list does not determine support. +- Give each input a unique title before upgrading. Calls with the same title + or fallback declaration key still produce separate manifest rows with the + same `title`; their `supported` flags do not detect the collision. Different + `group=` labels do not separate keys. On the paired 1.4.0 engine's normal + checked path, a completed run of such a script has `fingerprint: null` even + without an override. Supplying the shared key fails before execution with + `setting_rejected`, reason `ambiguous_key`. The retained request + `Period="7"` returned a success report on 1.3.0, whose legacy setter applied + a shared key to all matching inputs. Legacy-path unresolved provenance rows + may still appear inside a fingerprint; that differs from the whole + fingerprint being null on the checked path. +- The paired engine's run harness now uses the checked settings API. Stored + presets and sweep values must satisfy the compiled input's type, bounds and + options. Two other retained requests that succeeded on 1.3.0 now fail with + `setting_rejected`: `Color="color.blue"` (`expected_integer`) and + `Choice="15"` for options `[10, 20, 30]` (`invalid_input_option`). A color's + manifest still has `type: "string"` and a Pine spelling such as `"color.red"` + as its default, but the checked native input requires the packed integer; + do not forward that manifest string as a native color value. The checked + API also validates parseable numbers, booleans, enum members and known keys. + This does not mean all invalid settings previously succeeded: + `initial_capital="abc"` and `initial_capital="-1"` already failed on 1.3.0; + they now report `expected_finite_decimal` and `value_below_minimum` reasons. + Legacy C setters retain their existing behavior. - The paired engine adds `strategy_get_last_error_code` and `strategy_get_last_error_args` beside the existing English error getter, under `PINEFORGE_HAS_RUN_FAILURE_CODES_V1`. Its run harness exposes `code` @@ -58,6 +90,10 @@ set them. when the English message is empty; do not accept a stopped run's partial output as success or parse the English to recover a code. These run failures are distinct from transpile-time `CompileError` diagnostics. + The setting refusals above are catalog class `input`; a deliberate + `runtime.error` is `strategy_runtime_error`, class `strategy`. The installed + engine entrypoint returns exit 4 for both classes: exit 4 alone does not + select a billing or refund decision. - The paired engine certifies scalar input/override types in fingerprint provenance; unresolved values remain explicit rather than being treated as certified native values. This is an engine report change, not another field @@ -118,13 +154,13 @@ set them. `bool` default publishes a boolean, not a number. - A plain `input(-5)` or `input(-2.5)` is typed `int` or `float` with its number as `default`, where it was `string` with `null`. The compiled - getter and checked receipt keep their existing float type for plain - numeric inputs; this manifest correction does not change that lowering. + getter and checked receipt keep their existing float type for these + signed plain inputs; this manifest correction does not change that lowering. Values the receipt computes at run time (`LEN * 2`, `not true`, `timestamp(year, month, ...)`) stay unpublished: the default remains - `None`/JSON `null`, an unfoldable bound is omitted, and an unsupported - numeric options expression can leave an empty list. An `input.color` + `None`/JSON `null`, an unfoldable bound is omitted, and the whole `options` + field is omitted when any numeric choice is unfoldable. An `input.color` retains its Pine spelling as a string default, not the receipt's packed integer; other nonliteral plain `input()` calls remain `string` with no published default (the source case above is the exception). A folded @@ -170,11 +206,26 @@ The hardening wording remains scoped to the previously documented emitted-string validation and escaping; these edge-case repairs and regression checks are not a new arbitrary-source security guarantee. No exploit payloads are included. +### Known issues with consumers + +The new numeric options expose an exact-type comparison in +`pineforge-hpo` 0.11.0: a JSON integer `20` is refused with +`invalid_input_option` for an `input.float` choice `20.0`, although the +numbers are equal. This can affect a caller that reads the manifest's +default and sends it back through JavaScript's ordinary JSON serialization, +which writes a whole-number value as `20`, losing its float spelling. +Preserve a floating-point value at the HPO boundary, for example `20.0` in +explicit JSON or a Python float. Writing `20.0` in JavaScript before ordinary +serialization does not preserve that distinction. Upgrade to the planned +HPO 0.11.1 fix or a later release containing it when available; that fix is +separate from this engine/codegen release and is not yet claimed released. + ### Recorded validation and grades The landed #190 candidate recorded 9,254 engine-enabled pytest passes with 12 audited skips, 2,164 focused passes each under GCC and Clang without skips, -314 public corpus compilation passes, and 279 JSON/Pyodide fixtures. Those +314 public corpus compilation passes, and 279 fixtures in each of the JSON +and Pyodide fixture-parity checks. Those are the request's retained native measurements, not a new native suite run for this documentation change. The release-pair baseline records excellent: 7,983; diff --git a/README.md b/README.md index 42e1d75..3215a3b 100644 --- a/README.md +++ b/README.md @@ -69,9 +69,21 @@ a release. After the 1.4.0 pair is published, regenerate C++ and relink against engine `v1.4.0`'s generated headers and runtime, even though C ABI version 4 and the script ABI epoch are retained. Emission and input manifests change; neither -generated C++ nor Python/Pyodide envelopes are promised byte-identical. Use -`inputs[].supported` for UI/optimization eligibility and account for corrected -source/enum choices, string values, signed numeric bounds and plain-input types. +generated C++ nor Python/Pyodide envelopes are promised byte-identical. Every +input row now has `supported`: when false, hide it, exclude it from sweeps and +optimization, and do not send a value. Its compiled default is used; a supplied +value is refused with `setting_unsupported`. A true flag still requires type, +bounds and options validation, and does not detect duplicate keys. Account for +corrected source/enum choices, string values, signed bounds and plain-input types. + +Give each input a unique title; different `group=` labels do not separate +override keys. The paired checked path refuses a shared key and returns a null +fingerprint for a completed run of such a script even without an override. +The [migration notes](CHANGELOG.md#compatibility-and-migration) cover this change, +new refusals of some previously accepted settings, and color's string manifest +versus packed-integer native value. Check stored presets before submitting them. +Users of `pineforge-hpo` 0.11.0 must also preserve floating-point option values +at its input boundary; see the [known issue](CHANGELOG.md#known-issues-with-consumers). The paired engine adds coded run failures and typed fingerprint provenance. Its run harness returns failure `code` and typed `args` beside `error`; an @@ -208,10 +220,11 @@ returns the first three keys; `requests` is new in 1.1.0). `inputs` is the input manifest; its `title` is the actual override key, and an `input.symbol` entry also has `"kind": "symbol"` (since 1.1.0). `diagnostics` contains nonfatal warnings. Since 1.4.0, each input also has `supported`, the checked-settings receipt's -flag, so a consumer can hide the inputs the compiled strategy cannot honour -and keep them out of parameter sweeps and optimization before running -anything. Defaults, bounds and choices are the receipt's values where the -receipt holds a literal: string inputs spell a built-in constant by its runtime +flag. Hide inputs with a false flag, exclude them from sweeps and optimization, +and do not send a value: the compiled default is used, and the paired engine +refuses a supplied value with `setting_unsupported`. A true flag is not a full +validation or duplicate-key check. Defaults, bounds and choices are the +receipt's values where it holds a literal: string inputs spell a built-in constant by its runtime value (`alert.freq_all` is `"all"`, not the Pine name), a source or enum input lists its choices, and a typed number input publishes a signed or named-constant default and bound and the numbers of an `options=[...]` dropdown. An @@ -226,8 +239,9 @@ embedded NUL truncates the published default/choice at the first NUL; it is not a lossless-string promise. Literal Unicode and escaped backslashes are retained. Run-time arithmetic, `not true` and calendar-part `timestamp` values still have no published default or unfoldable bounds; a folded -`timestamp("...")` is a literal. Numeric computed choices can leave an empty -list, and colors retain their Pine spelling, not their packed native value. +`timestamp("...")` is a literal. The whole numeric `options` field is omitted +if any choice is unfoldable. Colors retain their Pine spelling and `string` +manifest type, while the checked native setter requires a packed integer. The [contract](docs/PUBLIC_CONTRACT.md#input-manifest-and-override-keys) lists these retained limits. The PyPI and Pyodide manifests carry the same fields; no key is renamed or removed, and the changelog lists the values that changed. @@ -668,8 +682,9 @@ generated before the source-layer cut 0.10.4's included, derives from `BacktestEngine` and does not compile against engine `v1.4.0`; old cap-only C++ does not attach the priority rule, and metadata cannot silently restore it. Rebuild all modules against the -new matching C++ layout (`engine_script_run_v19` in engine `v1.4.0`); old -fingerprint versions are not comparable. The extraction preserves Pine policy +new matching C++ layout (`engine_script_run_v19` in engine `v1.4.0`). Retaining +that epoch does not promise equal fingerprints, state hashes or trades across +releases. The extraction preserves Pine policy under explicit attachment; it does not implement the generic native child-activation scheduler or prove campaign neutrality. Compile-only corpus checks do not run Pine backtests. diff --git a/docs/PUBLIC_CONTRACT.md b/docs/PUBLIC_CONTRACT.md index 63576b3..ae8ee36 100644 --- a/docs/PUBLIC_CONTRACT.md +++ b/docs/PUBLIC_CONTRACT.md @@ -145,6 +145,26 @@ This is a runtime failure channel, distinct from the `CompileError` and `diagnostics` contract of transpilation. Regenerate and relink for the new pair; C ABI version 4 alone does not authorize reusing a 1.3.0 strategy library. +The paired 1.4.0 harness uses the checked settings API for generated libraries; +the 1.3.0 harness used the legacy setters. Some requests that previously +returned success now fail before execution: a shared `Period` key set to `"7"` +is `setting_rejected` with reason `ambiguous_key`, textual color `"color.blue"` +is `expected_integer`, and `"15"` outside choices `[10, 20, 30]` is +`invalid_input_option`. The color manifest's `string` type and Pine-spelling +default do not describe the native setter's packed-integer encoding. Validate +presets against the compiled settings, including types, bounds and options; +the checked path also refuses unknown keys and invalid numeric, boolean or +enum values. An input with `supported: false` rejects a supplied value with +`setting_unsupported`; leave it at its compiled default. + +These examples do not imply that every invalid request used to succeed. +`initial_capital="abc"` and `initial_capital="-1"` already failed on 1.3.0; +the paired engine now reports `setting_rejected` with +`expected_finite_decimal` and `value_below_minimum`, respectively. Setting +refusals are catalog class `input`; `strategy_runtime_error` is class +`strategy`. The installed engine entrypoint maps both to exit 4, so that exit +alone does not determine billing or refund treatment. + ## Optional generated settings extension From 1.1.0 on, C++ compiled against an engine providing @@ -326,10 +346,13 @@ them identically. string input whose default or a choice is neither a string literal nor a known built-in constant (`size.small`, `position.top_right`, `na`, a reassigned name), or an `input.enum` whose default or choices are not literal members of - one enum. Hide such inputs, and keep them out of sweep and optimization - spaces, before creating a strategy. Key on `supported` alone: every unsupported + one enum. Hide such inputs, exclude them from sweeps and optimization, and + do not send a value: the strategy uses its compiled default, and the paired + engine rejects a supplied value with `setting_unsupported`. Use `supported` + to determine eligibility rather than `options` emptiness: every unsupported string input has `options: []`, with or without `options=`, and so does a - supported input written `options=[]`. + supported input written `options=[]`. A true flag does not validate a value's + type, bounds or choices, or detect duplicate override keys. - A string input (`input.string`, `.timeframe`, `.session`, `.symbol`, ...) publishes the lowered strings: a built-in constant its runtime value (`alert.freq_all` is `"all"`, `currency.USD` `"USD"`, `format.price` @@ -339,7 +362,7 @@ them identically. descriptor decodes only the emitter's C++ string-literal escape grammar, preserves literal Unicode and escaped backslashes, and stops at the first embedded NUL, matching native `std::string(const char*)` construction. The - emitter spells NUL as fixed three-digit octal `\000`, including before + emitter spells NUL as fixed three-digit octal `\000` (one backslash), including before digits. This is current manifest/receipt consistency, not lossless storage of a string containing NUL. - A source input (`input.source(hl2)`, a plain `input(close)`) publishes its @@ -353,20 +376,24 @@ them identically. numeric `default`, `min`, `max`, `step` and the numeric choices of an `options=[...]` dropdown where the receipt's value is a literal: a signed number (`-2.5`), a named constant (`LEN = 14`, `minval=-RATIO`) or `timestamp("2024-01-02T00:00:00")` of a string literal. A - plain `input(-5)` or `input(-2.5)` is typed `int` or `float` by its literal, as - `input(5)` is; the compiled getter reads it as a double, so the receipt's - type for it is `float`. An `input.bool` default is a boolean when the + plain `input(-5)` or `input(-2.5)` is typed `int` or `float` by its literal; + these signed forms retain a double getter and a `float` receipt type. + A plain `input(5)` uses an int getter and receipt. + An `input.bool` default is a boolean when the descriptor lowers it to literal `true` or `false`, including an inlined constant; `not true` remains a run-time expression. - Not published, because the receipt computes it at run time: arithmetic over constants (`LEN * 2`), a call (`math.pow(2, 3)`), `timestamp(year, month, ...)` (it reads the symbol's time zone) and `not true`. Such a `default` is - `None` and such a `min` or `max` is omitted. Numeric choices containing - an unfoldable expression can publish `options: []`, not its computed - values. These gaps are pinned in `tests/test_input_metadata_receipt.py`'s - `KNOWN_LIMITS`. An `input.color`'s + `None` and such a `min` or `max` is omitted. The whole numeric `options` + field is omitted when any choice is unfoldable. These gaps are pinned in + `tests/test_input_metadata_receipt.py`'s `KNOWN_LIMITS`; its test view maps + an absent options field to `[]`, which is not the public manifest shape. + An `input.color`'s `default` is its Pine spelling (`"color.red"`) and its `type` `string`, where - the receipt holds the packed integer. Apart from the source-input case + the receipt holds the packed integer required by the checked native setter. + Do not submit the manifest's Pine-spelling string as a native color value. + Apart from the source-input case above, a plain `input(...)` whose default is not a literal is typed `string` with a `None` default. @@ -380,9 +407,16 @@ The `title` is the **actual override key read by the emitted C++**: For example, `length = input.int(14)` has key `"length"`, and `ta.ema(close, input.int(9, "Fast"))` has key `"Fast"`. The manifest includes -both declared and inline global-scope calls. If several calls share a key, -one override sets all of them; the translator emits a warning naming the -colliding inputs. Different `group=` labels do not separate override keys. +both declared and inline global-scope calls, one row per call. If several +calls share a title or fallback key, those rows retain the same `title` and +their per-input `supported` flags do not reflect the collision. The translator +warns about the colliding inputs. The legacy setters apply a shared override +to all matching inputs; the checked setters refuse the key as ambiguous. +On the paired 1.4.0 engine's normal checked path, a supplied shared key fails +with `setting_rejected`, reason `ambiguous_key`; a completed run with no such +override still reports the whole `fingerprint` as `null`. This differs from +legacy-path unresolved provenance rows, which may remain inside a fingerprint. +Give each input a unique title. Different `group=` labels do not separate keys. An explicit title that is not a compile-time string constant raises `CompileError`. From 9ed00c4639ad813ab43ad16436c54be4986d21ff Mon Sep 17 00:00:00 2001 From: luisleo526 Date: Thu, 8 Oct 2026 09:42:41 +0800 Subject: [PATCH 3/3] docs: correct failure classes and HPO option guidance Supersede c203463's overbroad setting-refusal class statements with the b3192bfc catalog mapping for setting_rejected, setting_unsupported and strategy_runtime_error. Keep the exit-4 billing/refund limitation. Correct c203463's HPO workaround to follow each manifest option's actual number type, including integer and mixed options on float inputs. Use public examples instead of retained-request wording and bind the README migration link to the 1.4.0 section. Keep notes Unreleased; refresh dating separately without applying it to this branch. Co-Authored-By: Codex --- CHANGELOG.md | 35 +++++++++++++++++++++-------------- README.md | 6 +++--- docs/PUBLIC_CONTRACT.md | 9 +++++---- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e8325c..cb0d1af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,14 +65,14 @@ set them. `group=` labels do not separate keys. On the paired 1.4.0 engine's normal checked path, a completed run of such a script has `fingerprint: null` even without an override. Supplying the shared key fails before execution with - `setting_rejected`, reason `ambiguous_key`. The retained request + `setting_rejected`, reason `ambiguous_key`. For example, `Period="7"` returned a success report on 1.3.0, whose legacy setter applied a shared key to all matching inputs. Legacy-path unresolved provenance rows may still appear inside a fingerprint; that differs from the whole fingerprint being null on the checked path. - The paired engine's run harness now uses the checked settings API. Stored presets and sweep values must satisfy the compiled input's type, bounds and - options. Two other retained requests that succeeded on 1.3.0 now fail with + options. Two other examples that succeeded on 1.3.0 now fail with `setting_rejected`: `Color="color.blue"` (`expected_integer`) and `Choice="15"` for options `[10, 20, 30]` (`invalid_input_option`). A color's manifest still has `type: "string"` and a Pine spelling such as `"color.red"` @@ -90,10 +90,10 @@ set them. when the English message is empty; do not accept a stopped run's partial output as success or parse the English to recover a code. These run failures are distinct from transpile-time `CompileError` diagnostics. - The setting refusals above are catalog class `input`; a deliberate - `runtime.error` is `strategy_runtime_error`, class `strategy`. The installed - engine entrypoint returns exit 4 for both classes: exit 4 alone does not - select a billing or refund decision. + `setting_rejected` is catalog class `input`; `setting_unsupported` is class + `unsupported`. A deliberate `runtime.error` is `strategy_runtime_error`, + class `strategy`. The installed engine entrypoint returns exit 4 for all + three codes: exit 4 alone does not select a billing or refund decision. - The paired engine certifies scalar input/override types in fingerprint provenance; unresolved values remain explicit rather than being treated as certified native values. This is an engine report change, not another field @@ -211,14 +211,21 @@ a new arbitrary-source security guarantee. No exploit payloads are included. The new numeric options expose an exact-type comparison in `pineforge-hpo` 0.11.0: a JSON integer `20` is refused with `invalid_input_option` for an `input.float` choice `20.0`, although the -numbers are equal. This can affect a caller that reads the manifest's -default and sends it back through JavaScript's ordinary JSON serialization, -which writes a whole-number value as `20`, losing its float spelling. -Preserve a floating-point value at the HPO boundary, for example `20.0` in -explicit JSON or a Python float. Writing `20.0` in JavaScript before ordinary -serialization does not preserve that distinction. Upgrade to the planned -HPO 0.11.1 fix or a later release containing it when available; that fix is -separate from this engine/codegen release and is not yet claimed released. +numbers are equal. For HPO 0.11.0, send each selected value with the number +type the original manifest publishes for that option: `20.0` when it lists +`20.0` (explicit JSON `20.0` or a Python float), and integer `20` when it lists +`20`. A float input written with whole-number-literal options such as +`options=[10, 20, 30]` publishes integer options; mixed lists retain mixed +number types. The input's `float` type alone does not choose the workaround. + +This can affect a caller that reads a float-spelled manifest default and +sends it back through JavaScript's ordinary JSON serialization, which writes +a whole-number value as `20`, losing its float spelling. Writing `20.0` in +JavaScript before ordinary serialization does not preserve that distinction; +preserve each option's original number type at the HPO boundary. Upgrade to +HPO 0.11.1 or a later release containing the fix when available. This fix is +separate from the engine/codegen release; HPO 0.11.1 has not been released at +the time of writing. ### Recorded validation and grades diff --git a/README.md b/README.md index 3215a3b..e43ab3b 100644 --- a/README.md +++ b/README.md @@ -79,11 +79,11 @@ corrected source/enum choices, string values, signed bounds and plain-input type Give each input a unique title; different `group=` labels do not separate override keys. The paired checked path refuses a shared key and returns a null fingerprint for a completed run of such a script even without an override. -The [migration notes](CHANGELOG.md#compatibility-and-migration) cover this change, +The [1.4.0 migration notes](CHANGELOG.md#140--unreleased) cover this change, new refusals of some previously accepted settings, and color's string manifest versus packed-integer native value. Check stored presets before submitting them. -Users of `pineforge-hpo` 0.11.0 must also preserve floating-point option values -at its input boundary; see the [known issue](CHANGELOG.md#known-issues-with-consumers). +Users of `pineforge-hpo` 0.11.0 must preserve the manifest's number type for +each option at its input boundary; see the [known issue](CHANGELOG.md#known-issues-with-consumers). The paired engine adds coded run failures and typed fingerprint provenance. Its run harness returns failure `code` and typed `args` beside `error`; an diff --git a/docs/PUBLIC_CONTRACT.md b/docs/PUBLIC_CONTRACT.md index ae8ee36..be1aacc 100644 --- a/docs/PUBLIC_CONTRACT.md +++ b/docs/PUBLIC_CONTRACT.md @@ -160,10 +160,11 @@ enum values. An input with `supported: false` rejects a supplied value with These examples do not imply that every invalid request used to succeed. `initial_capital="abc"` and `initial_capital="-1"` already failed on 1.3.0; the paired engine now reports `setting_rejected` with -`expected_finite_decimal` and `value_below_minimum`, respectively. Setting -refusals are catalog class `input`; `strategy_runtime_error` is class -`strategy`. The installed engine entrypoint maps both to exit 4, so that exit -alone does not determine billing or refund treatment. +`expected_finite_decimal` and `value_below_minimum`, respectively. +`setting_rejected` is catalog class `input`; `setting_unsupported` is class +`unsupported`. A deliberate `runtime.error` is `strategy_runtime_error`, +class `strategy`. The installed engine entrypoint returns exit 4 for all +three codes: exit 4 alone does not select a billing or refund decision. ## Optional generated settings extension