Repository navigation
Releases
v1.12.0
v1.12.0
Changes since v1.11.0
- chore(release): v1.12.0 (6e27d0f)
- Correct two changelog entries and say "work item" in the P8 row (ff67778)
- Fail the run when a timezone lookup cannot be carried out (874585b)
- Build an N-day chart on the year-restart N-day grid (#829) (9aacd54)
- Document the current CI job graph (#838) (526416b)
- Fail the run on an unaccepted timezone or a failed void setter (#837) (e163980)
- Fail the run when str.match or a calendar lookup meets a memory failure (#831) (cdb8ae8)
- Fix engine execution cases and selected-capacity admission for 1.12.0 (#825) (360c4ab)
- Sync runtime history portably on macOS (#798) (f5e929d)
Prebuilt libraries
Linux amd64 and arm64 static-lib + headers tarballs are attached below (C ABI).
For the full PineScript -> backtest image, use ghcr.io/pineforge-4pass/pineforge-release.
Run-failure codes
run_failure_codes-v1.12.0.json is the run-failure code catalog this release publishes;
run_failure_codes_diff-v1.12.0.json lists its changes since v1.11.0.
v1.11.0
v1.11.0
Changes since v1.10.0
- chore(release): v1.11.0 (a447232)
- ci: pass Eigen3_DIR with its PATH type to the release configure (#796) (5191950)
- ci: admit the typed Eigen3_DIR flag in the release step contract (#795) (d1c1934)
- Date the two runtime history codes to 1.11.0 (#793) (a3964ad)
- Reuse scratch search routes and make native live-object limits exact (#791) (33503b8)
- ci: move release toolchain builds to hosted runners (#777) (e1d29cb)
- Build sanitizer shards from their selected CTest producers (#785) (67e1e80)
- Combine pull requests 781, 758, 780 and 783 with the corpus validation, trade text and RCI changes (#788) (0c8e1a8)
- docs: clarify baseline promotion requirements (#778) (755e665)
- Combine pull requests 774, 764, 765, 773, 772 and 759 (#776) (63f3978)
- docker: say what the compile guard can and cannot tell about a limit stop (#775) (c6a61ef)
- engine: preserve cohort admission ordering and capacity (#770) (822641a)
- Bind an exit without from_entry to the market entry placed before it in the same execution (#769) (06eb496)
- ci: run release and notify-hub hosted and compare glibc with the v1.10.0 archive (#768) (b09f79d)
- Add SeriesPercentileNearestRank for a series length of ta.percentile_nearest_rank (#767) (be584eb)
- ci: admit a hosted form for the release jobs with protected credentials (#771) (a43b0ae)
- Add the run option series_origin_ms: declare where the reference series starts (#766) (36dd032)
- tests: align adapter offset pins with placement storage growth (#763) (c9a6414)
- ci: give hosted sanitizer builds dedicated disk space (eb29cf5)
- docs: use maintainer in runner reference (929c213)
- docs: name a maintainer in runner guidance (3c9f7bb)
- ci: move engine verification jobs to hosted Ubuntu (e7fa559)
- Restore layout pins and source-only placement test registration (d9e787e)
- Merge pull request 756 (keep sparse placement inserts amortized O(1)) (d398279)
- Merge pull request 751 (honor quantity grids in default entry size queries) (37cf309)
- Merge branch 'main' into perf/placement-storage (b948ce3)
- Merge branch 'main' into fix/default-entry-quantity-query (d150f41)
- Merge branch 'main' into ci/unittest-shards-names (819b200)
- Honor quantity grids in default entry size queries (3065060)
- Make the ledger storage status row independent of concurrent disk writes (d6391d8)
- Merge commit '9ee1cc7d5e53568279d882900f96c21f5ddde055' into perf/placement-storage (67aad88)
- ci: rename run_unittest_shards scripts (63b5d56)
- Record the placement table's moved growth sites (d25e8d2)
- Keep sparse placement inserts amortized O(1) (075d86f)
- Merge branch 'main' into fix/script-bar-observer-missing-bucket (9ee1cc7)
- Merge branch 'main' into fix/script-bar-observer-missing-bucket (261ef72)
- Merge branch 'main' into ci/docs-counts-native-site-v2 (164e6ca)
- Refuse a primary row without a script bucket in the script-bar counter (2109ea9)
- docs: correct post-fleet count pins after b08fc6f wording change (c640419)
- Merge branch 'main' into ci/doc-reverts-locale (f81e1f6)
- Keep quantity-query registration before the preflight fence (e9212ac)
- Add observational default entry quantity query (24f75b9)
- Take main into this branch (0a8ca1e)
- Add selected-capacity bounds, bounded drawings and native history storage (ba14721)
- test: force C locale for doc-revert git fixtures (449c8b6)
- docs(live): clarify the empty chart clock in ratio metadata (14e166a)
- Merge main 3080b40 into the ledger ratio metadata fix (dfc285a)
- fix(live): preserve ratio metadata for optional native clocks (fe5eb7a)
- fix(live): configure native ledger behavior fixtures (e79fe31)
- ci: retain native-x86 allowlist expectations after #742 merge (3080b40)
- Merge main aff553b into feature/ledger-delta-stream-2 (a0ded75)
- Merge main aff553b into #742 (41f08d2)
- Add indexed ledger delivery and delta-v2 reports (68c1a58)
- Merge commit '9e42fe999948cc06ece23acde0d5bde803888cbc' into ci/ondemand-labels (aff553b)
- Merge commit '9e42fe999948cc06ece23acde0d5bde803888cbc' into ci/x86-release-required (4737131)
- ci: keep release-gate build pins on on-demand runners (91b773f)
- ci: cover on-demand runner-label refusals (3570f2d)
- ci: keep on-demand rollback note in pinned docs shape (cbcbcd2)
- ci: retry lost on-demand shards (7f99a31)
- ci: keep CI-script labels after CTest label clearing (9e42fe9)
- ci: route post-merge build and sanitizer shards to on-demand runners (9f4ad0c)
- Scope aggregate mutations to their intended jobs (5efac4a)
- Complete native x86 aggregate regression fixtures (693443d)
- Align native x86 CI admission and regression fixtures (740c5b7)
- Require native x86 GNU 13.3 Release builds in CI (e1ef745)
- Revert "Keep ci-script labels after CTest label clearing" (923f7aa)
- Keep ci-script labels after CTest label clearing (5140571)
- ci: retry runner-loss failures for same-repository PR runs (69c2b46)
- Shard arm64 post-merge verification without dropping engine tests (49bfbc1)
- docs: record the grading profiles and sweep limits (#732) (144d02c)
- Fix prebuilt glibc ancestor directory identity (91b6672)
- Preserve archive compatibility and token-only prebuilt publication (bd1fa2f)
- ci: harden runner-loss retry classification (ace3079)
- docs: rewrite CLAUDE.md and AGENTS.md as a short Testing-first guide (09756a9)
- Run sanitizer checks across eight shards (4c9e763)
- ci: re-run aggregate gates that failed only because of runner loss [skip ci] (fc58eec)
- ci: run the release path on on-demand runners (12148f9)
- docs: use neutral wording for historical performance examples (#728) (cf68f15)
- ci: set up Node before the docs deploy on the protected GKE runner [skip ci] (8e1fcf3)
- ci: re-run failed post-merge jobs lost to GKE Spot reclaims [skip ci] (b35a5d9)
Prebuilt libraries
Linux amd64 and arm64 static-lib + headers tarballs are attached below (C ABI).
For the full PineScript -> backtest image, use ghcr.io/pineforge-4pass/pineforge-release.
Run-failure codes
run_failure_codes-v1.11.0.json is the run-failure code catalog this release publishes;
run_failure_codes_diff-v1.11.0.json lists its changes since v1.10.0.
v1.10.0
Pairs with codegen v1.10.0 (pip install pineforge-codegen==1.10.0).
1.10.0
Fixed
- Prevent 32-bit narrowing in the checked integer-parameter forms (#285, #322).
- Preserve time-input values in the checked changed-input, unchanged-
varand copied-input forms, while preserving the directly used ordinary unchanged date filter (#321, #322). - Prevent an extra reversing lot on the same tick after
strategy.closewhencalc_on_order_fills = trueandpyramiding = 1(#708, #727). - Fill a stop-limit entry on the closing tick with
process_orders_on_close = truewhen its stop is reached and its limit is executable at that close (#725, #707). - Render Boolean-to-string conversions as
trueorfalse, and preserve Boolean inputs read through history (#319, #315). - Compile the reported single-line-body and string-array
na()forms (#290, #281). - Allow a same-ID market entry after
strategy.closewhen an earlier same-ID limit entry on that bar was not reached (#723, #727). - Align
request.securitybar-index values for supported requested-timeframe grids, including 300-, 420- and 600-minute requests on a 60-minute chart (#722, #727). - Correct
time_closefor session-bound feeds when the caller omits the symbol session and timezone (#720, #727). - Evaluate the selected
request.securitytimeframe on each bar for a known finite set of alternatives, including computed strings and local aliases (#316). - For supported per-bar requests, preserve independent persistent state for each requested timeframe, including a referenced persistent counter declared outside the request (#314).
str.format_timepreserves quoted spans, renders a single unquotedZas a numeric UTC offset, and safely sizes long formatted results (#712).- Long stop-limit orders fill when observed tick input reaches an executable fractional limit on the chart's price grid, including affected limits such as 99.99 on a 0.01 grid (#721, #715).
- Preserve a fractional division used directly as a loop start, and count supported floating-step loops using TradingView's measured end rule (#317).
- Preserve fractional quotient reassignments in supported integer-initialized variables, including supported function-local return values (#318).
- Supported statically endless counted loops at chart scope stop with a located runtime error when their result is used; the measured unused form is omitted (#317).
- Accept
math.powkeyword arguments,int/float/bool/stringnamed-argument labels, constant-colour input defaults and supported operator-line continuations (#317).
Engine and tooling updates
- Native-live modules built with an earlier version are still accepted and use the standard report path (no live accumulator) until they are rebuilt.
- Renamed the borrowed-array row type from
FeedBartoFeedBarRow, avoiding clashes with client-definedFeedBartypes (#717). - Serialized provider fetches per source repository in
ci_verify, preventing concurrent verification workers from racing on the same repository (#717). - Added container-level quota supervision for run-json jobs (#717).
- Updated public documentation and comments with plain language, synthetic examples and neutral wording (#712, #717).
Changed run-failure codes
Consumers that validate diagnostic argument values need to accept these additions in the minor release:
outputs_limit:args.reasongainstoo_many_series. This code also covers a series-capacity limit reached at declaration.scratch_capacity_exceeded:args.resourcegainscohort_ids_totalandcohort_orders_live.
Known issues
Building from source with Clang 18
- Building 1.10.0 from source with Clang 18 and libstdc++ 13 fails on Linux (verified on arm64) at
src/native_calc_plan.cpp:231. GCC builds and the prebuilt binaries are not affected. Workaround: build with GCC or use a prebuilt. Fixed in the next release (#779).
Linux x86_64 prebuilt and glibc
- Engine 1.10.0's
linux-x86_64prebuilt requires glibc 2.38 or newer; on older glibc, loading a strategy fails. Theaarch64prebuilt is unaffected. A fix is planned for 1.11.0 (#730).
Two strategy modules in one process and local time
- When two strategy modules, each linked with its own copy of the engine archive, run in one process, a time-of-day value in one module (for example
hour(t, "America/New_York")) can be computed in the other module's timezone. The wrong value comes with no error. Running one strategy module per process is unaffected (#735).
Array aliases and copied slices
Some valid array scripts compile but produce values different from TradingView:
- An array alias kept past its source array's lifetime produced
2.0where TradingView produced3. - A copy of an array slice that still shares elements produced
2.0where TradingView produced1.
In each checked example, none of the 26 bars matched TradingView.
Changed time inputs
A non-var variable initialized from input.time() and changed later keeps accumulating the changes instead of restarting from its input on each bar. The width checks for this shape compare only the first bar; per-bar restart remains a separate defect (#322).
Indicators start cold at the backtest start
App backtests, HPO, hosted MCP and offline npm runs currently start the engine at the user's first bar, with no earlier history. A TradingView chart normally includes earlier bars, so its indicators are already warm. Indicators that depend on history can therefore differ during the first part of a run.
One measured example, not a general bound: on one ETH 15-minute script with 192 trades, TradingView had 288 bars of history before the selected start. EMA values differed for about the first 73 fifteen-minute bars (36 half-hour samples) and RSI for about the first 172 bars (86 samples); window indicators such as SMA and highest had no values for about the first 11 bars (5 samples). Loading those same 288 earlier bars made all 192 rows equal on every field. These counts are not a warm-up guarantee for other scripts or settings.
Separate comparisons against references that start at the range start found two extra early trades for an EMA-crossover strategy and differences in the first four MACD quantities. The trade impact of those strategies against a normal chart with longer history has not yet been measured.
This long-standing behavior is confirmed in 1.8.0, 1.9.1 and 1.10.0. Compiler note PF-W1505 has said “ta.ema initial warmup is approximated” since 1.0, without quantifying the difference.
Loading history before the selected start addresses the cold start; the existing selected-window pre-roll is an opt-in for this. It matched TradingView in the 192-row example above. No broader workaround has yet been verified against TradingView. For references that themselves start at the range start, an upcoming release is planned to add a run option to declare the series origin. This does not make a cold-start reference equivalent to a chart with earlier history.
For App backtests, the start-of-run difference will be fixed when the App loads history before the selected start (planned; no date yet).
On a daylight-saving change day, a requested intraday bar of a symbol with no trading session in a daylight-saving timezone may use the other side's offset for instants before the change; this is not yet measured against TradingView.
Some request.security calls inside helper methods, or with a payload that reads a helper-local value, translate but fail to compile; a fix is in progress.
T...
v1.9.1
v1.9.1
Upgrade notes
PF_ABI_VERSIONis 5, as in 1.8.0 and 1.9.0; the C ABI (pineforge.h) is unchanged by 1.9.1, so a strategy library built for 1.9.0 still loads. C++ hosts and generated strategies are rebuilt against the 1.9.1 headers and library as for every release.- Container entrypoint: a mounted
strategy.cppmust now be vouched for. With no/in/strategy.pinemounted, the entrypoint compiles/in/strategy.cpponly when the environment holdsPINEFORGE_STRATEGY_CPP_SHA256equal to the lowercase hex sha256 of exactly those bytes (taken over the frozen copy the compiler reads). Otherwise it ends with exit 2 and onerun_request_invalidJSON line on stdout (optiongenerated_cpp) before any compile.PINEFORGE_ALLOW_UNATTESTED_CPP=1keeps the old behaviour for local use; a hosted caller must not set it, and it should be set only on a container you start yourself. Mountingstrategy.pineis unchanged. - Typed scratch failures. A script container (string, array, map or object store) whose scratch growth or commit fails now reports the typed scratch failure instead of
engine_unclassified_errorwith the texto1 store: put/commit: io_error:report_sink_failedwithreason: arena_exhaustedwhen the scratch arena is exhausted, andreport_sink_failedwithreason: scratch_iowhen the disk scratch fails (a zeroRLIMIT_FSIZE,ENOSPCorEIOon the unjailed route); runs that did not fail are unchanged. A failed memory allocation in the scratch layer (astd::bad_alloc, or anothrowallocation that returned null, in the command and script scratch backings, the page cache, the arena directories and metadata, the store workspace, the external-opening readbacks, the owning journal decoders and the spilled order book) now fails the run with the typedout_of_memorycode instead ofscratch_io_failed,report_sink_failed,engine_invariantor an unclassified error; a real operating-systemENOMEMfrom a mapping or a disk operation, an extent limit and arena exhaustion keep their existing codes. The two owning journal-decode helpers declared innative_order.hppandsource/market_admission.hppare no longernoexcept: an allocation failure in them throws the typedout_of_memoryfailure instead of returning an I/O status. A caller that binds one of these functions to anoexceptfunction pointer must update it (since C++17,noexceptis part of the function type); the same goes for code that asserts they arenoexcept, and a caller inside anoexceptfunction now terminates if an allocation fails. Otherwise the change is source-compatible, and every C++ object must be rebuilt against the new headers as for any release. - Closed-trade text comparisons take one engine call.
TradeLedgergainsentry_id_matches,exit_id_matches,entry_comment_matchesandexit_comment_matches(an index outside [0,size()) compares as empty; the*_equalsmembers stay), and the fourBacktestEngine::closed_trade_*_equalswrappers now call them. A script that comparesclosedtrades.entry_id(i)with a literal over every closed trade on every bar does less work per visit. - The closed-trade read cache is paged. It no longer doubles with the number of trades and discards its entries at each doubling; slots keep a fixed mapping and the storage comes in 256-slot pages, so a whole-history scan no longer re-reads every row from disk after each cache growth. A non-retiring run of 64 rows or fewer holds about 62 KB more (one 256-slot page of 312-byte entries plus the 2,048-byte page table, against 64 entries before), and a retiring run holds at most (retained rows / 256, rounded up, plus 2) pages: three pages (about 240 KB, plus the table) for the default 64-row window; both amounts depend on the retained window, not on the number of trades.
cache_resident_bytes()andresident_cache_bytes(rows)both count the page table (2,048 bytes at the default cap of 65,536 rows). The memory-management page (docs/pages/memory-management.md) describes the paged cache and its charge. - Fewer scratch writes per closed trade. A row is written back only when its close cause changes (or the row is below the sealed base), an identical stored descriptor is not stored again, and the texts of one appended row go out as one contiguous write; retirement no longer zeroes on disk what the same operation's head release zeroes or truncates again. Reports are unchanged.
- The container's report wrapper does less work per row. The wrapper (
docker/run_json.py,docker/pf_sink.py) now validates sink trade lines with one plan per verified header instead of a generic per-field function, with the same accepted set, the same left-to-right refusal order and the same error text; it encodes bulk equity rows without a per-row template, with the same write boundaries and bytes; and bar tapes whose volume column holds integers, or whose prices are written with fixed decimals such as100.00, now take the packed loader instead of being declined, with the same bars, count and feed hash as the slower reader. Trade rows are encoded once and queued before they are written (the queue of each column file keeps the existing bounds of 512 records and 256 KiB, so nothing grows with the number of trades or bars), and the equity proof derives the repeat allowed by the run pattern once instead of once per batch. A write can therefore be attempted later than the row that queued it; a write failure is still published as the same typedreport_sink_failedrefusal with reasonscratch_io, at the same point relative to the other refusals. The final report artifact, which the wrapper writes once and reads back into stdout, now has a 1 MiB aligned I/O window instead of 64 KiB; the columns, the spools and the sink reader keep 64 KiB, and the bytes written are unchanged. That is one fixed 1 MiB window buffer per report prepared, independent of the report size, the number of recorded output series and the number of trades. Reports are byte-identical. The change is in the container's Python files only (docker/run_json.py,docker/pf_sink.py); no C++ object and no C ABI header is affected. - A cohort's target is read from an index built once per generation. A strategy that keeps many lots open and has live exits used to canonicalize every physical opening again for each cohort target it read, several times per ordinary OHLC point. The engine now canonicalizes each physical opening once per populated generation, indexes the openings by root in a private fixed-size array (at most 4,096 rows of 16 bytes, 64 KiB, kept across invalidations), and answers a cohort's target read from the range of its own root. The original reader stays as the complete fallback: above 4,096 open lots, on an allocation failure, for intrabar and tick input, inside the source host's applied callback, and when the index cannot be built for the current state. The array is reserved on the first indexed cohort read, even for a few lots, and its size does not depend on bars, trades or lots. Reports, fills, ids and hashes are unchanged. This entry makes no speed claim.
- The closed-cohort origin release searches the roster union once per pass. After a close, the adapter's origin-release pass decides which request origins can be released. For each open lot it asked, roster by roster, whether the lot's opening was a member of that roster, so a pass could touch every roster once for each lot. It now collects the origins of all rosters once into one vector, sorts it, removes duplicates and answers each lot with a binary search; the ordered commands the pass produces are unchanged. The vector is private to the adapter, holds at most 8 bytes per stored roster origin (up to about 16 bytes per origin while it grows), is cleared at the start of every pass and keeps its capacity between passes; it has no fixed cap, because its size follows the number of roster origins the engine already stores, and it is at most about twice that count (the capacity high-water mark), so it adds no growth with bars or trades beyond what the rosters already hold. No public header changes and no installed class changes layout. This entry makes no speed claim.
- The closed-trade ledger writes about half as often to its scratch files in the measured case. In the measured case each closed trade cost four writes on the disk scratch route, two to the index file and two to the payload file, because the retired head of each file was zeroed after every publication. The retired head is now zeroed in whole 4,096-byte blocks behind a per-file watermark. Counted on the closed-trade stream test fixture (a strategy that closes one trade on every bar: 6,400 closed trades, both ids 4 bytes, a report sink writing to a disk file; not a general rate) against the engine just before this change, which already includes the fewer-writes change above (1.9.0 was not measured), the write system calls to the ledger's two scratch files, index and payload together, fell from 4.004 to 2.048 per closed trade (from about 2 to about 1 on each file). Up to 4,095 bytes of the newest retired index bytes (about 24 index records) and up to 4,095 bytes of the newest retired payload bytes per file stay un-zeroed until the next block completes or a complete chunk is returned; the scratch files are anonymous, unlinked and never reopened, and a hosted arena keeps those bytes in the run's own chunk (until it is returned or the run ends). Reports are byte-identical (pinned on the stream test fixture; the 312-probe parity sweep did not move). No public header, class layout,
PF_ABI_VERSIONor stream layout changes. The arena routes make nopwrite; the same block rule applies to their memory writes, where the saving is a memory copy of 4 KiB of zeros, not a syscall. is_na()andna()accept strings, containers and nullable handles. Ininclude/pineforge/na.hpp, only arithmetic types had anis_naoverl...
v1.9.0
v1.9.0
Upgrade notes
PF_ABI_VERSIONstays 5. A 1.8.0 strategy library still loads (same ABI), and the closed-trade read changes below need no rebuild of it. The supported pairing is exact, so regenerate strategy C++ with codegen 1.9.0 and relink against the 1.9.0 headers andlibpineforge.a. C++ that codegen 1.9.0 generates with the new in-place closed-trade text comparison builds only against engine 1.9.0.- Live runner: the report stream defaults are unchanged.
pineforge-live run --report-jsonland--report-jsonl=fullkeep writing the cumulative report document per committed input;--report-jsonl=deltais opt-in. - Live runner: report checkpoints. The runner writes immutable cumulative report checkpoints at K, 2K, 4K, ... committed inputs (
--checkpoint-interval K, default 1024). Opening an existing ledger writes its first checkpoint during startup recovery and reads the full history once. 1.9.0 adds areport_checkpointstable to the ledger, and a 1.8.0 runner does not accept a ledger that has it, so copy the ledger file first if you may need to go back. Report export and restart verify the chain from the nearest checkpoint;report --verify-fullre-verifies all of it. A ledger without integrity digests still opens and needs one verifiedrunreplay before export. - Live runner: optional ratio interval and an optional module export.
--ratio-interval K(default 1, unchanged behaviour) refreshes the four ratio fields every K inputs for modules that export the new optionalstrategy_stream_engine_layout_versionand matchPF_STREAM_ENGINE_LAYOUT_VERSION; other modules keep the full-snapshot path. - Live runner: webhook delivery. A separate thread now drives HTTP while the delivery worker handles the ledger; this addresses accepted deliveries timing out while the worker waited for the ledger during a checkpoint.
ended_atis stamped when the delivery worker records the result, so after a ledger stall a successful attempt'sended_at - started_atcan exceedtotal_timeout_ms; timeout settings, retry policy, acknowledgement and recovery are unchanged. - Run-failure codes: ten added, none changed. Three run-quota codes (
run_descriptor_limit_exceeded,run_quota_configuration_invalid,run_resource_quota_exceeded) and seven report-channel codes, numbered 70 to 79; existing numbers, arguments and exit statuses are unchanged. Nothing emits the seven report-channel codes yet. The run-quota contract (host inputsPF_RUN_SCRATCH_BYTES,PF_RUN_SCRATCH_FILES,PF_RUN_OUTPUT_BYTES,PF_RUN_DESCRIPTOR_LIMIT) is documented in the run-failure-codes page; the helpers define admission, and their presence does not establish that every producer or launcher in a build uses them. - Closed-trade reads. C++ callers can compare closed-trade ids and comments in place, embedded NUL bytes included, without allocating strings, and non-retiring ledgers serve sealed closed-trade rows from retained storage.
- Headers changed since 1.8.0 include
include/pineforge/pineforge.h,include/pineforge/engine.hpp,include/pineforge/trade_ledger.hppandinclude/pineforge/run_failure_codes.hpp, and the newinclude/pineforge/detail/run_quota.hpp(not a complete list; compare thev1.8.0andv1.9.0tags). - macOS: the CMake file now lets one symbol,
strategy_reads_trade_history, stay undefined when linking the shared window-plan library on macOS (the 1.8.0 known issue). The project's CI does not build or test macOS and macOS is not a supported platform: the engine is built, tested and published for Linux only.
Known issue: some scripts run slower than in 1.6.3
In one measurement of five test scripts on one host, run time against 1.6.3 ranged from about 0.7 to 3.4 times; the cause is not established for most of them. A script that reads the closed-trade list on every bar, over 35,000 bars, took about 1.9 times as long: a median of about 28.0 s against about 14.6 s on 1.6.3, with the slowest of four repetitions at 31.95 s. Its run ended with the same typed report_too_large failure on both versions. 1.8.0 was not part of this measurement. A speed fix is planned for a later release; there is no date yet.
Full notes: CHANGELOG, 1.9.0.
Changes since v1.8.0
- chore(release): v1.9.0 (ec44968)
- docs: correct the report stream default in the changelog and date 1.9.0 (#639) (99bf1d5)
- ci: declare the gkp-8cpu-16gb-arm64 runner label for actionlint (#638) (ecc08ee)
- engine 1.9.0: closed-trade text reads, run quota contract, report channel failure codes, live runner report checkpoints (#637) (f0b6213)
- ci: mask the engine version in the replay-off golden pins (#634) (d24a823)
Prebuilt libraries
Linux amd64 and arm64 static-lib + headers tarballs are attached below (C ABI).
For the full PineScript -> backtest image, use ghcr.io/pineforge-4pass/pineforge-release.
Run-failure codes
run_failure_codes-v1.9.0.json is the run-failure code catalog this release publishes;
run_failure_codes_diff-v1.9.0.json lists its changes since v1.8.0.
v1.8.0
v1.8.0
Upgrading
Before upgrading a live runner, copy its ledger file if you may need to go back to 1.7.0: the upgrade migrates it one way.
Known issue
macOS: linking the pineforge_window_plan shared library needs -Wl,-U,_strategy_reads_trade_history; fixed in 1.9.0.
Changes since v1.7.0
- chore(release): v1.8.0 (9836309)
- ci: refuse a workflow file on a gke- or gkp- label that the runner-group mirror does not list (#633) (6dd5287)
- Combined 1.8.0 batch 2: reversal fix, report iteration, replay companion, metrics-only mode (#632) (62a0b70)
- runner: accept a pre-integrity ledger of the current layout on open (#631) (a34a0d0)
- ci: pin that the repository's own workflow files pass its admission rules (#624) (11eae42)
- Combined 1.8.0 batch: scratch caches, live-runner reports, adapter scaling, UDT graphs, metrics replay and the CI admission fix (#616) (a991b4e)
- Keep the trade-ledger cache at its default cap on report-sink runs and assemble the report in one pass (#590) (1654497)
- Land a stack of ten engine changes: paged strings and maps, ABI 5, report-sink vectors, release environments and parallel preflight (#609) (f85ce18)
- docs, comments and diagnostics: describe behaviour in plain words instead of internal tracking labels (#580) (d55b116)
- tests: balance the ownership-manifest control shards by checker work, drop the stale 180 s wording (#585) (e4d6703)
- tests: attribute scratch-file creations to the test's own process tree (#584) (7bc74d2)
- docs: declare the selected-window path public for walk-forward use, with a doc-lint rule that holds it (#564) (58c42ca)
- comments: say what the code does in four places that said something else (#569) (d590ca1)
- ci: run the post-merge light jobs on GKE runner pods (#592) (b08fc6f)
- ci: write the GKE runner as the bare scale-set name (#571) (482b000)
- C ABI: strategy_initial_capital, so the report writer names simple_proportional_overflow in every run (#565) (95f4fd1)
- ci: run the secret-free post-merge Linux jobs in GKE runner pods (#562) (c85a40b)
- scratch: keep a run's command scratch resident up to a fixed size; sync the report sink once at close (#557) (8713a82)
- run_json: buffer the jailed and fd report text as UTF-8 bytes (#559) (4dc08f4)
- memory: state the report-memory bound exactly and add the proofs for it (resident, disk, jailed arena) (#556) (ff05c78)
- run_json: verify and copy path-sink equity records in bulk (#553) (6b33c47)
- engine: Pine arrays stored on the paged object layer, flat in the number of bars (#547) (4319d52)
- ci: move post-merge verification to GCP and forbid paid runners (#555) (8154993)
- ci: source allocation-bound reading table: verdict for the 108 defect and unsure rows (second pass) (#550) (f506849)
- tests: native command-after probe checks each journal event once (#548) (feae336)
- ci: split the long test rows, shard the full CI legs and run them on the larger hosted runners (#544) (3ea5a0d)
- docs: memory-management page re-read against main after the 1.8.0 changes (#546) (30e9f9a)
- docs: supported platforms are Linux x86_64 and arm64 from 1.8.0 (#545) (f66318a)
- docker: name the memory and scratch controls in the entrypoint's usage text (#543) (bf0811b)
- docs: a hosting layer may re-place an arena_exhausted run; the development service does not (#542) (53e884a)
- Report-sink typed failure, opt-in report descriptor and run profile diagnostics, dependency-scratch storage fault code (#536) (4c113fc)
- engine 1.8.0: cohort tables with per-event work independent of pending orders, live order-key guard, id admission and session fixes (#541) (27cd77b)
- report sink: read trades and equity in one raw sequential pass each (#529) (2cd229f)
- ci: drop the macOS test legs and release slice; add Linux arm64 test legs (#532) (6c95d82)
- docs: add the memory-management page (#531) (6d2bf95)
- docs: CHANGELOG 1.8.0 section with migration notes (#527) (fd7013e)
- release gate: require a green ci-post proof run for the released commit (#526) (d463222)
- tests: attribute scratch-tmpfs watcher events to this process, not co-tenants (#521) (434b98c)
- check_kernel_residuals: a bare, uncorroborated strings-only line is not a residual (macOS) (#525) (4b41eae)
- ci: dispatch-only post-runner capability canary (fleet-post-capability.yml) (#520) (641349a)
- tests: drop the "fast" label from the pytest row test_run_json_stream_fastpath (#522) (2446e86)
- test: compare borrowed fd flags without Darwin's written bit (#519) (b7e16f5)
- docs: state scratch placement and its refusals in one place (1.7.0 and later) (#518) (2561cfb)
- run_json: cut the wrapper's report-sink passes from ~11 to 3 (part of S-272) (#517) (2fdf2f5)
- test_check_doc_reverts: do not inherit the caller's PINEFORGE_DOC_REVERT_BASE (#516) (29c544f)
- engine: fixed-shape O(1) store for Pine collections (A5-STORE) (#515) (180630f)
- docs: backfill release notes for engine releases (#510) (6cfea80)
- docs(AGENTS): CI runner routing after the split (companion to #512) (#513) (61dccd8)
- ci: run the light secret-free CI jobs on a pinned GitHub-hosted image (CI-RUNNER-SPLIT, R309 + R312) (#514) (b0f4341)
- ci(live): run corpus equivalence as a required CTest row (#481) (dad2530)
- ci: source allocation-bound gate over the engine and generated C++ (scoped site identities, fail-closed bounded admission) (#469) (f95e244)
- ci: pin every third-party action to a full commit SHA with a version comment, and lint it in preflight (ORG-ACTIONPINS) [replaces #495] (#508) (be9f20d)
- tests: drop the TIMEOUT 180 ctest property (owner's no-time-limit rule, P2-CM-F4) (#504) (0a43c2c)
- ci(release): the permission-claim text guard also catches backslash-wrapped, bytes-decoded and fullwidth respellings (PROBE-FU) [replaces #493] (#506) (f8ec601)
- ci: fully qualify the two remote-tracking refs the promote-baseline ancestry check and the doc-reverts default base read (ORG-ACTIONPINS-CARD) [replaces #494] (#507) (db58170)
- ci: release ruleset probe lists every branch ruleset (measurement only, gate unchanged) [replaces #489] (#505) (19350a8)
- test: the run-failure diff tests follow the diff's own start, so they hold before and after a release reset (#509) (7a7d099)
Prebuilt libraries
Linux amd64 and arm64 static-lib + headers tarballs are attached below (C ABI).
For the full PineScript -> backtest image, use ghcr.io/pineforge-4pass/pineforge-release.
Run-failure codes
run_failure_codes-v1.8.0.json is the run-failure code catalog this release publishes;
run_failure_codes_diff-v1.8.0.json lists its changes since v1.7.0.
v1.7.0
v1.7.0
Changes since v1.6.3
- chore(release): v1.7.0 (ed9e08e)
- CHANGELOG 1.7.0: known limit on macOS (fd report sink, 64 MiB stream RSS) (#503) (77b87af)
- Build on macOS: fdatasync via F_FULLFSYNC, mkdtemp via <unistd.h> (1.7.0 tag blocker) (#501) (4ae3f88)
- 1.7.0 release prep: CHANGELOG 1.7.0 — 2026-10-25, stale 'unreleased' pointers, macOS build fix (::sig*set macros) (#500) (b250ceb)
- CR-150 O(1) report integration: the engine 1.7.0 line as one commit over main 9eff925 (91353de)
- test(live): serialize startup replay webhook deliveries (#463) (9eff925)
- ci(release): read-only ruleset probe for the release gate (CR-245 F1) (#488) (9c18d41)
- ci: promote-baseline preflight rejects workflow-level defaults/env (#482 follow-up) (#483) (a2bb907)
- ci(release): narrow release-bump exemption in the release gate (CR-245, G13, v2) (#486) (72d9fd5)
- ci: run both ctest label-exclusion tests; refuse duplicate test names (CR-146) (#407) (b7bea58)
- ci: skip suite jobs on draft PRs; the gate fails on drafts (LB-387) (#466) (948d393)
- docs(AGENTS): current CI runner labels and PR vs post-merge split (#484) (83c1ecb)
- ci: promote-baseline triggers on main push / workflow_run, not pull_request_target (CR-231) (#470) (833f7ac)
- ci: promote-baseline refuses untrusted heads and actors (SEC846) (#482) (717c75d)
Prebuilt libraries
Per-arch static-lib + headers tarballs are attached below (C ABI).
For the full PineScript -> backtest image, use ghcr.io/pineforge-4pass/pineforge-release.
Run-failure codes
run_failure_codes-v1.7.0.json is the run-failure code catalog this release publishes;
run_failure_codes_diff-v1.7.0.json lists its changes since v1.6.3.
Known issues in 1.7.0 (a stall and extra CPU on trade-heavy runs; upgrade to 1.9.1 or later)
- Stall on scripts that read closed trades on every bar (1.7.0 only; fixed in 1.8.0). In 1.7.0 a run that writes its report through the report sink kept only 128 closed trades in its read cache, so a script that loops over
strategy.closedtrades.*(i)on every bar re-read every row from disk on every access. A 175,000-bar script of that kind finished in 59 s on 1.6.3 and did not finish within 900 s on 1.7.0. From 1.8.0 on, report-sink runs keep the same 65,536-row cache cap as every other run (#611, landed with #590), so 1.8.0, 1.9.0 and 1.9.1 carry the fix. A pre-release build of 1.9.0 finished that same script but took about 3.2 times as long as 1.6.3; the extra time was not split between the loop and the report step, and the published 1.9.0 and 1.9.1 were not measured on it. - Extra CPU in the container image's report wrapper on runs with many trades (1.7.0; reduced in later releases, not shown to be gone). Measured on one script (24,238 trades, 175,000 bars) with its per-bar loop removed, the run took about 60 s on 1.7.0 against about 4 s on 1.6.3, and most of the extra time was spent in the Python report wrapper (
docker/run_json.py), not in the engine library. 1.8.0 assembles the report in one pass over the sink (#590; about 14.6 s in the same test, measured on that pull request's head, not on the 1.8.0 tag), and 1.9.1 does less work per trade row again (#648, #649, #652). A further reduction is being worked on.
v1.6.3
v1.6.3
Changes since v1.6.2
- chore(release): v1.6.3 (e799ac8)
- ci(cr233): split the PR wall from full-event verification (CR-233 A+B, squash of #477) (#480) (3a0d150)
- ci(release): require full verify + parity green before an engine release (C2) (#479) (ad1ebac)
- test(live): match delivery receipts by request sequence (CR-223) (#478) (c32c732)
- tests: stop naming a private repository in the session tape comment (M3 engine) (#476) (20267dd)
- ci: run the engine's CI on the GCP fleet (pf-gcp-pr / pf-gcp-main), fork-gated (#474) (a8089b2)
Prebuilt libraries
Per-arch static-lib + headers tarballs are attached below (C ABI).
For the full PineScript -> backtest image, use ghcr.io/pineforge-4pass/pineforge-release.
Run-failure codes
run_failure_codes-v1.6.3.json is the run-failure code catalog this release publishes;
run_failure_codes_diff-v1.6.3.json lists its changes since v1.6.2.
v1.6.2
v1.6.2
Changes since v1.6.1
- chore(release): v1.6.2 (dfe5f36)
- ci: native-live and the ubuntu Debug leg as pool-only required PR checks under the CI canon (#464) (21ba62b)
- CI: defer macOS Debug from pull requests (D3, CR-195) (#454) (eed6598)
- ci: release jobs run on pf-ci-release (owner: CI on our machines) (#461) (03b2406)
- ci: remove timeout-minutes (owner #827, no time limits) (#462) (7d79006)
- ci: pin the actions of promote-baseline.yml by SHA, to the pins codegen #223 landed (#443) (88ec1b3)
- CR-202: proportional annualisation for sub-day runs, never null (W-73) (#456) (34f9b3a)
- CI: PR tier excludes the slow rows on Release and kernel-only, with registration floors, and defers the slow verifier self-test classes (D1, CR-195) (#449) (0212cab)
- ci: cancel only superseded pull-request runs of the docs workflow (#444) (da9403f)
- chore(release): v1.6.2-rc.1 (7bd065c)
- tests: fix the EN-115 ownership probes so proof B reads 118/118 (CR-139) (#446) (18d8f3c)
- tests: shard the ownership manifest-controls test 4 ways so Debug/ASan stop timing out (CR-196) (#445) (375795a)
- selected-window: declare contract revision 1.3 with the EN-115 completeness proof (1.6.2) (#412) (39d6fa1)
- ci: App-token owner follows the repository owner (org rename readiness) (#439) (7d2c9fd)
- ci: run Linux x64 jobs on the pf-ci-spot pool (#420) (3c19a69)
- ci: run every job on the standard runners (cost stop until ci-spot) (#415) (693fd9e)
Prebuilt libraries
Per-arch static-lib + headers tarballs are attached below (C ABI).
For the full PineScript -> backtest image, use ghcr.io/pineforge-4pass/pineforge-release.
Run-failure codes
run_failure_codes-v1.6.2.json is the run-failure code catalog this release publishes;
run_failure_codes_diff-v1.6.2.json lists its changes since v1.6.1.
run_failure_codes_diff-v1.6.2-from-v1.6.2-rc.1.json lists them since v1.6.2-rc.1, the release candidate before this release:
the step in the chain of diffs through every release, candidates included.
Build note: linux-x86_64 archive
The pineforge-v1.6.2-linux-x86_64.tar.gz archive was built by the maintainers outside CI, because a CI runner restriction stopped the release job's Linux x86-64 leg. It was built from tag v1.6.2 (commit dfe5f36) with the release workflow's own build steps in an Ubuntu 22.04 userland with GCC 11.4.0, the same compiler and glibc floor as v1.6.1. SHA-256: 7f66c21750982ddcb476758bcf711b9f474a66de60bb658581ba9f0e4853019f.
v1.6.2-rc.1
v1.6.2-rc.1
Changes since v1.6.1
- chore(release): v1.6.2-rc.1 (7bd065c)
- tests: fix the EN-115 ownership probes so proof B reads 118/118 (CR-139) (#446) (18d8f3c)
- tests: shard the ownership manifest-controls test 4 ways so Debug/ASan stop timing out (CR-196) (#445) (375795a)
- selected-window: declare contract revision 1.3 with the EN-115 completeness proof (1.6.2) (#412) (39d6fa1)
- ci: App-token owner follows the repository owner (org rename readiness) (#439) (7d2c9fd)
- ci: run Linux x64 jobs on the pf-ci-spot pool (#420) (3c19a69)
- ci: run every job on the standard runners (cost stop until ci-spot) (#415) (693fd9e)
Prebuilt libraries
Per-arch static-lib + headers tarballs are attached below (C ABI).
For the full PineScript -> backtest image, use ghcr.io/pineforge-4pass/pineforge-release.
Run-failure codes
run_failure_codes-v1.6.2-rc.1.json is the run-failure code catalog this release publishes;
run_failure_codes_diff-v1.6.2-rc.1.json lists its changes since v1.6.1.