Repository navigation
engine 1.8.0: cohort tables with per-event work independent of pending orders, live order-key guard, id admission and session fixes - #541
Merged
Conversation
…g orders, live order-key guard, id admission and session fixes Part of #452. Credit: the "walk live cohorts only" shape follows the external proposal in #458. This is the engine side of the 1.8.0 integration: the cohort-table work for runs with many pending orders, the guard on live order keys with its optional run-failure argument, and four smaller fixes. It targets engine 1.8.0. ## Behaviour changes users can see - Jailed profile: an id longer than 64 bytes is refused before any store mutation with scratch_capacity_exceeded ("id longer than 64 bytes"). The jailed default scratch manifest follows a 64 MiB share (id table 262,144 / 32,768 / 65,536 entries), mirrored in docker/run_json.py. The resident floor of a jailed run rises by about 51 MiB (the slab is zero-filled at construction: 61,128,704 B computed, +52 MB measured as jailed peak RSS on a dense run), a fixed floor, not growth with bars. The jailed ceiling for distinct entry ids moves from 4,096 to 65,536. Unjailed runs are unchanged. - Two live order keys whose 64-bit hashes collide are refused with engine_invariant instead of one silently replacing the other. engine_invariant gains an optional vocabulary argument `reason` with the single value order_key_hash_collision; every other engine_invariant still carries no argument, no code is added or removed, and hosts should accept (not pin the absence of) the optional argument. The catalog diff shows one changed code. - Strategy ids, entry selectors and OCA names that contain NUL or the unit separator (0x1f) are refused at admission with pine_invalid_argument (rule unsupported_combination), because composite exit and replacement keys join two user strings with 0x1f. - A session string with more than eight windows is parsed completely (it stopped at the ninth window, so the daily close fell back to the first window only). Up to eight windows keep the fixed-size storage and allocate nothing. - The per-chart range vectors of the aux security feed give their capacity back at terminal points of a batch run. ## Cohort tables - A full close no longer seeks the scratch index once per owner for every bracket family: BracketRoster::remove_selected checks the in-memory incarnation counts first. A close on N owners goes from N(N+1)/2 seeks to 2N+1. On the dense shape the old code counted 8,385 and 33,153 find_key calls where the new code counts 257 and 513 (a test run, counts printed by the test, not seconds). - DiskResidentIdTable keeps an ordered held set of the resident rows that hold an opening or live units (held_rows()), so the walks that need only those rows no longer visit every pending-only cohort. Credit: the proposal in #458. - Resident cohort lookup is O(log R) without allocation or disk; the implicit spill is amortised; kMaxResidentCohorts = 4096 non-spillable cohorts, jailed profile only; refusal texts state the live manifest limit and a workaround. Measured on a release build of this change against engine main, on a 1m ETH-USDT feed, 4 repetitions per cell (2 at 60,000 and 100,000 bars), 600 s cap per run: the dense script at 4,000 bars goes from 83.9 s to 6.4 s jailed and from 85.8 s to 7.4 s unjailed; the same reports are produced (normalised report hashes equal in all 16 cells where both finished). At 10,000 bars and above the dense script does not finish within 600 s on either tree, so this change does not make the dense shape scale: the per-bar retire pass is still Theta(R) in the number of resident rows, and the close event is still Theta(N^2) in cheap in-memory work (only the scratch seeks became linear). A script with many orders and few ids is 13, 12.5 and 16 percent faster jailed at 30,000, 60,000 and 100,000 bars. ## Live order-key guard Before a live handle is selected or overwritten, the identity captured for the key is compared with the placement ids. Represented spellings are checked on insert, replace, lookup and erase; a spelling the table does not classify keeps the previous hash-only behaviour and never causes a refusal. A new test walks every represented spelling through a public run, and a check confirms the strict missing-spelling diagnostic text is absent from the built library archive in every build configuration. ## Source allocation gate The source-bounds gate gains entries for the new container sites: the unreviewed ceiling goes from 1,241 to 1,264 and the pinned OPEN count from 1,429 to 1,452. None of the new entries claims a bound; this is not a flat-memory proof. ## Proof The engine verification steps (preflight, release, debug, sanitizers, kernel, docs, parity, subset) all pass on this tree; parity measures 312 probes with 312 matching the baseline and none moved. The private-fixture rows of the touched tests, including test_run_failure_codes, pass with the fixtures overlaid. ## Replaced documentation text In docs/pages/scratch-tables.md two texts from commit 91353de are replaced on purpose by the new constants (they are not reverts): the sentence "The key/byte choices came from an admission policy using 4,096 roster entries and 256-byte components; they are aggregate descriptor limits, not a new per-string length check", and the arithmetic `1+96*4096+1048576 = 1,441,793` (now `1+96*65536+4194304 = 10,485,761`). Co-Authored-By: letiep97 <[email protected]> Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
….0 integration branch No conflicts. The only file both sides changed is docker/run_json.py: this branch changes the jailed default scratch manifest in default_scratch_manifest, main changes the report-sink passes; the hunks are apart and merge cleanly. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This was referenced Oct 26, 2026
luisleo526
added a commit
that referenced
this pull request
Oct 26, 2026
This was referenced Oct 26, 2026
luisleo526
added a commit
that referenced
this pull request
Oct 27, 2026
…it (resident, disk, jailed arena) (#556) memory: state the report-memory bound exactly and add the proofs for it (resident, disk, jailed arena) The memory-management page said the default sink route keeps the equity curve out of memory, but not what still grows with the bar count, and that a jailed run without --report-sink-fd has no sink. It now states, with sources: the resident equity-report buffers are bounded on the default unjailed sink route; the curve scratch costs 24 B per script bar on disk (6 MiB at 262,144 and 48 MiB at 2,097,152 bars) with the E output and the JSON artifact; in a jailed run the scratch is the RAM-backed arena, so the same 24 B per script bar are memory there, sized by the hosting wrapper and failing closed with arena_exhausted; sink-off callers keep the native curve plus an owning report copy. "O(1) in bars" is not claimed for the jailed arena or the sink-off path. Removed text, named. The page text below was introduced by 6d2bf95 (docs: add the memory-management page, #531) and 30e9f9a (docs: memory-management page re-read against main after the 1.8.0 changes, #546): - the section "Use the report sink for bounded memory" is replaced by "Report memory: resident buffers, disk and the jailed arena" (its line citations were re-read and corrected); - the phrase "a bounded default route is planned, with no release date" and the sentence "Install a report sink; no release date for the default route" are removed from the report-memory table (the page documents the route as it is); - the two sink rows of that table are reworded to separate resident buffers from disk and arena growth; the sentences about the heap bound of the equity curve and the ledger, the sink file growth, and the legacy FULL sink route are rewritten into the new section, and the release container's default profile sentence of 30e9f9a is replaced by the route table. Tests: a two-size resident proof for the report scratch curve (N = 262,144 and 8N script bars, RSS growth within 1 MiB, scratch growth exactly 24 B per added bar, metric block bit-equal to the sink-off result); a corpus projection test that the temporary-sink route and the sink-off route give bit-identical metrics and diagnostics on real modules (it replaces the named-sink summary comparison on corpus modules that this test did before, and still skips when its manifest variable is unset). Both report-memory rows are pinned as slow rows (61 pinned names) and registered before the CR-150 include block. The binary-route peak-RSS row is left out of this change: its 9-minute wall exceeds the CI leg budget. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> ci: count the array-growth slow row with the report-memory slow row (62 pinned names) Both the report-memory change and the paged array store added one pinned slow row each on top of 60, and git merged the two identical edits of the count into 61. The pinned set now holds 62 names: 43 from the PINEFORGE_PR_SLOW_TESTS loop and 19 direct CMake LABELS assignments. The test assertion on len(PINNED_SLOW_TEST_ROWS) and the four sentences in docs/ci.md that state the count and its split (introduced by 3ea5a0d, ci: split the long test rows, shard the full CI legs and run them on the larger hosted runners, #544, and rewritten by the report-memory change) are updated to 62 and 43 + 19. No other text is removed. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> ci: update the slow-row count literals in test_ci_preflight to 62, 43 and 19 test_documented_slow_row_counts_match_literal_population pins the three numbers that docs/ci.md states for the pinned slow rows (all, from the PINEFORGE_PR_SLOW_TESTS loop, direct LABELS assignments) and two sentences that repeat them. The previous commit moved the documented count from 61 to 62 (43 + 19) but left this test at 61, 43 and 18, so test_ci_preflight_slice_6 failed on every profile. The test now states 62, 43 and 19. Only those literals change. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> memory: move three docker/run_json.py citations after the bulk equity copy The bulk path-sink equity copy added code above the report-sink helpers in docker/run_json.py, so the three line ranges the memory-management page cites for _ReportSink, _build_sink_report and _prepare_stream_report no longer contained their symbols (doc-anchors: SYMMISS). The ranges now read 6183-6220, 7102-7148 and 6983-7003. Citation numbers only: no sentence is added, removed or reworded. Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Also rewritten: the 60-name slow-row counts in docs/ci.md introduced by 3ea5a0d (ci: split the long test rows ..., #544), now 62 = 43 + 19 (the report-memory row and #547's array-growth row); the Pine product host row of the report-memory table in docs/pages/memory-management.md introduced by 27cd77b (engine 1.8.0: cohort tables ..., #541), reworded to separate resident buffers from disk and arena growth.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #452. Credit: the "walk live cohorts only" shape follows the external proposal in #458.
This is the engine side of the 1.8.0 integration: the cohort-table work for runs with many pending orders, the guard on live order keys with its optional run-failure argument, and four smaller fixes. It targets engine 1.8.0.
Behaviour changes users can see
reasonwith the single value order_key_hash_collision; every other engine_invariant still carries no argument, no code is added or removed, and hosts should accept (not pin the absence of) the optional argument. The catalog diff shows one changed code.Cohort tables
Measured on a release build of this change against engine main, on a 1m ETH-USDT feed, 4 repetitions per cell (2 at 60,000 and 100,000 bars), 600 s cap per run: the dense script at 4,000 bars goes from 83.9 s to 6.4 s jailed and from 85.8 s to 7.4 s unjailed; the same reports are produced (normalised report hashes equal in all 16 cells where both finished). At 10,000 bars and above the dense script does not finish within 600 s on either tree, so this change does not make the dense shape scale: the per-bar retire pass is still Theta(R) in the number of resident rows, and the close event is still Theta(N^2) in cheap in-memory work (only the scratch seeks became linear). A script with many orders and few ids is 13, 12.5 and 16 percent faster jailed at 30,000, 60,000 and 100,000 bars.
Live order-key guard
Before a live handle is selected or overwritten, the identity captured for the key is compared with the placement ids. Represented spellings are checked on insert, replace, lookup and erase; a spelling the table does not classify keeps the previous hash-only behaviour and never causes a refusal. A new test walks every represented spelling through a public run, and a check confirms the strict missing-spelling diagnostic text is absent from the built library archive in every build configuration.
Source allocation gate
The source-bounds gate gains entries for the new container sites: the unreviewed ceiling goes from 1,241 to 1,264 and the pinned OPEN count from 1,429 to 1,452. None of the new entries claims a bound; this is not a flat-memory proof.
Proof
The engine verification steps (preflight, release, debug, sanitizers, kernel, docs, parity, subset) all pass on this tree; parity measures 312 probes with 312 matching the baseline and none moved. The private-fixture rows of the touched tests, including test_run_failure_codes, pass with the fixtures overlaid.
Replaced documentation text
In docs/pages/scratch-tables.md two texts from commit 91353de are replaced on purpose by the new constants (they are not reverts): the sentence "The key/byte choices came from an admission policy using 4,096 roster entries and 256-byte components; they are aggregate descriptor limits, not a new per-string length check", and the arithmetic
1+96*4096+1048576 = 1,441,793(now1+96*65536+4194304 = 10,485,761).🤖 Generated with Claude Code