PineForge unreleased
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
native_order.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "execution.hpp"
6#include "market_driver.hpp"
10
11#include <algorithm>
12#include <array>
13#include <cmath>
14#include <cstddef>
15#include <cstdint>
16#include <exception>
17#include <functional>
18#include <iterator>
19#include <limits>
20#include <map>
21#include <memory>
22#include <optional>
23#include <stdexcept>
24#include <string>
25#include <type_traits>
26#include <utility>
27#include <variant>
28#include <vector>
29
30namespace pineforge::detail {
31class ScratchSlab;
33}
34// The matcher (src/native_execution_consumer.hpp) is declared in the engine
35// epoch, as run_failure.hpp and execution_consumer.hpp declare epoch classes,
36// so the friends below name that one class and no other.
37namespace pineforge {
38struct CacheCapacity;
39inline namespace engine_script_run_v19 {
40class NativeExecutionConsumer;
41}
42}
43
45inline namespace native_order_v8 {
46
47// Isolated working-request/value core: current LIVE requests and immutable
48// command history. It does not own positions, cash, paid fees, matching,
49// calendar, host phase, or a second physical book.
50//
51// Identity types remain native_order_v1. Request/core/event values are v8.
52// Physical execution::Action is unchanged; native Reduce uses a typed size
53// source instead of a dummy units field. ExecutionPlan is a transient widening
54// used at the core/consumer boundary.
55
57/// A signed book transaction: finite nonzero units on the side their sign names.
58/// It opens, and it closes against a live opposite book on the way, so a
59/// transaction larger than an opposite position still opens the remainder. An
60/// opening denial rejects the ENTIRE transaction, including that remainder.
62using HandleVisitor = void (*)(void*, const RequestHandle&);
63
64/// A host-maintained, run-scoped roster identity. Zero is invalid and is
65/// never allocated by WorkingRequestCore.
67 std::uint64_t value = 0;
68};
69inline bool operator==(CohortHandle left, CohortHandle right) noexcept {
70 return left.value == right.value;
71}
72inline bool operator!=(CohortHandle left, CohortHandle right) noexcept {
73 return !(left == right);
74}
75inline bool operator<(CohortHandle left, CohortHandle right) noexcept {
76 return left.value < right.value;
77}
78
79/// Exact target exposure for an explicit reversal request. This is distinct
80/// from execution::ReverseTo, which is the transient resolved execution plan.
81struct ReverseTo {
82 double signed_units = 0.0;
83};
84
85enum class HostSizedKind : std::uint8_t { Open = 0, Close = 1 };
86enum class Side : std::uint8_t { Long = 0, Short = 1 };
87/// An opening or a closing whose quantity the HOST resolves, in
88/// NativeStrategyHost::resolve_execution_terms. `side` is required for an
89/// opening, which must declare where it trades. A host-sized request that reaches
90/// a candidate with no units answered is MatchRejectReason::TermsUnresolved. It is
91/// the adapter's sizing seam; a bare host that wants the kernel to resolve a cash
92/// or equity basis uses Sized instead. The C surface admits HostSized as a cohort
93/// close or a Book-scoped WaitForApplied close only (the 1.0 C boundary table).
94struct HostSized {
96 std::optional<Side> side;
97};
98
99/// Resolved host sizing normally remains subject to the run's quantity grid.
100/// A host may instead authenticate literal units for a pure reduction; the
101/// consumer still proves that the positive quantity is representable within
102/// the selected exposure before it can reach settlement.
103enum class ExecutionGridPolicy : std::uint8_t {
106};
107
108/// Native sizing bases (L3). The kernel resolves them into units at the
109/// sizing point. A source adapter lowers its own declaration-level default
110/// quantity onto them and keeps only its rounding and admission quirks
111/// (R5 R2); a host that owns its whole quantity still emits HostSized.
112struct CashValue {
113 double cash = 0.0; // account currency
114};
115/// A fraction in (0, 1] of MARKED equity at the sizing point. The other SizeBasis
116/// alternative, CashValue, is an absolute account-currency amount; both resolve as
117/// units = cash / (price * point_value * fx).
119 double fraction = 0.0; // of marked equity at the sizing point (0.10 = 10 %)
120};
121/// The two bases a Sized intent may name. Extended only by appending, so an
122/// existing aggregate initializer keeps its meaning.
123using SizeBasis = std::variant<CashValue, EquityFraction>;
124
125/// AtMatch resolves the basis at the matching candidate; AtAcceptance freezes
126/// the resolved units when the request is accepted.
127enum class SizeTime : std::uint8_t { AtMatch = 0, AtAcceptance = 1 };
128
129/// WHICH price the basis is converted at. This is the generic "size against the
130/// expected fill price" rule; it names no source language.
131///
132/// Resolved (today's behaviour) uses the price the kernel would otherwise
133/// settle at: the candidate's default resolved price for AtMatch, the raw
134/// acceptance-point price for AtAcceptance.
135///
136/// Signal is the decision-point price at placement -- the current bar's close,
137/// or the last print, at submit -- carried to the expected market fill: it is
138/// adjusted by the run's slippage for the request's own side
139/// (price +/- slippage_ticks * price_tick) and then rounded onto the run's
140/// price grid when NativeRunSpec::price_grid is on (HalfUp is the nearest
141/// tick; NativePriceGrid::None leaves it unrounded). It is frozen when the
142/// request is accepted, so it composes with SizeTime::AtAcceptance and, with
143/// SizeTime::AtMatch, the basis still converts at that frozen signal price at
144/// every later candidate. A Signal request accepted outside a decision point
145/// has no price to freeze and stays unresolvable.
146///
147/// SignalOnTick is the same decision-point rule measured on the INSTRUMENT's
148/// own tick ladder (NativeRunSpec::price_tick) instead of the run's fill grid:
149/// the decision price is rounded onto the ladder (nearest tick, ties away from
150/// zero), carried to the expected fill by the side's tick slippage, and rounded
151/// onto the ladder again. Two rulings are folded into it, both generic:
152///
153/// * price_tick is the instrument's own resolution and the run already
154/// declares it as the slippage multiplier, so a price the market PRINTS
155/// lives on that ladder whether or not the run also quantizes the prices it
156/// BOOKS. NativeRunSpec::price_grid governs fills; it does not govern what
157/// a signal was worth. A run that books unquantized fills can therefore
158/// still size against the printed price, which Signal cannot express.
159/// * the ladder is applied BEFORE the slippage, not only after. Slippage is
160/// a whole number of ticks, so it carries a ladder price to another ladder
161/// price: pre-rounding makes `round(p) +/- n*tick` exact and leaves the
162/// second rounding a pure binary64 re-normalization, where rounding only
163/// after composes two different quantizations of the same print.
164///
165/// Resolved and Signal are unchanged by this alternative, so every established
166/// Sized resolution keeps its value.
167enum class SizePrice : std::uint8_t { Resolved = 0, Signal = 1, SignalOnTick = 2 };
168
169/// A kernel-sized opening. units = cash / (price * point_value * fx), where
170/// cash is the basis value or fraction * marked equity, and price is the
171/// sizing-point price SizePrice names. A host override of
172/// resolve_execution_terms still has the last word: it sees the kernel-resolved
173/// units as the facts' RemainingUnits and may return its own.
174///
175/// SizeTime chooses WHEN the basis is resolved, SizePrice chooses WHICH price
176/// it converts at, and the two compose. Either way the request reaches the
177/// candidate with a deferred quantity and exactly one terms pass: the kernel
178/// resolves (or republishes the acceptance-frozen) units, publishes them as the
179/// facts' RemainingUnits before resolve_execution_terms and
180/// validate_execution_precommit run, and uses the host's units when the host
181/// returns any. An acceptance-time quantity is additionally an admission input
182/// at placement, so an opening the run cannot admit is
183/// RequestRejectReason::PlacementAdmission at submit rather than a rejected
184/// candidate later.
185struct Sized {
189 /// Which price the basis converts at; see SizePrice. Appended after `time`
190 /// because Sized has no positional aggregate initializer outside tests that
191 /// stop at the basis.
193 /// SnapToGrid floors the resolved units onto the run's quantity grid.
194 /// ExplicitUnits keeps the literal quotient, which the ordinary on-grid
195 /// terms gate then refuses when a grid is configured and the quotient is
196 /// off it; the two are identical on an ungridded run.
198 /// Divide the sizing cash by (1 + fee) when the run's fee kind is
199 /// NativeFeeKind::Percent. Every other fee kind is an exact no-op.
201};
202
203/// A reduction of exactly these units, finite and positive. Capped by the live
204/// exposure of the scope the reduce is bound to, and floored onto the run's
205/// quantity_grid like every other engine quantity.
207 double units = 0.0;
208};
209/// A reduction of exactly what the owner's fill opened, bound at the arm and
210/// reported by a QuantityBoundEvent. It is the arm-time size, which is why no
211/// SizeTime::AtArm exists.
213/// Gross claims the whole bound scope; NetOfSiblings first subtracts the units
214/// already claimed by the live sibling reduces bound to that same scope. The
215/// keys are opaque request/owner handles, never source identifiers.
216enum class ScopeClaim : std::uint8_t { Gross = 0, NetOfSiblings = 1 };
217/// Which measurement of the bound scope the fraction is taken of. AtMatch (the
218/// default) reads the scope as it stands at the matching candidate.
219/// AtAcceptance freezes the scope SIZE when the request is accepted -- the
220/// placement-time live basis -- so two 50 % siblings on one 10-unit lot both
221/// claim 5 under Gross even after the first has already executed. NetOfSiblings
222/// then subtracts the live sibling claims from that frozen basis.
223enum class ScopeBasis : std::uint8_t { AtMatch = 0, AtAcceptance = 1 };
224/// A fraction in (0, 1] of the bound scope, resolved at the matching candidate.
225/// The resolution is units = scope * fraction, one binary64 multiplication: a
226/// caller that spells its size as a percent converts percent -> fraction
227/// itself, so no second rounding step is introduced here.
229 double fraction = 1.0;
231 /// Appended last so every existing aggregate initializer keeps its meaning.
233};
234/// How much a Reduce takes: literal units, the owner's own opening, or a fraction
235/// of the bound scope.
236using ReductionSize = std::variant<ExplicitUnits, OwnerOpenedUnits, ScopeFraction>;
237/// A closing-only intent. It never opens, so it stays legal while the run's
238/// opening limits are already exceeded, and it uses CURRENT exposure rather than
239/// the acceptance-cycle book. On a flat book it terminalizes a NoEffectEvent: no
240/// execution identity, no fill, no fee, no physical action.
244/// What a request does. Six alternatives: Flatten (quantity-free whole-book
245/// close), Reduce (closing only), Transact (a signed book transaction), ReverseTo
246/// (an exact signed target exposure), HostSized (the host resolves the quantity)
247/// and Sized (the kernel resolves it from a cash or equity basis). The variant's
248/// size is pinned by the frozen C++ ABI fixtures, so appending an alternative is
249/// an epoch decision.
250using OrderIntent = std::variant<Flatten, Reduce, Transact, ReverseTo, HostSized, Sized>;
251
252/// Match at the next eligible matching point, with no level to reach. A market
253/// request accepted on bar N cannot fill on that bar's already delivered opening.
254struct Market {};
255/// `fill_through` makes the limit a touch trigger (market-if-touched): the
256/// level still gates when the request becomes executable, but its fill is not
257/// bounded by the level, so slippage may carry it past the level.
258struct Limit {
259 double price = 0.0;
260 bool fill_through = false;
261};
262/// A stop trigger: the modeled path has to reach `price` from the adverse side. A
263/// crossing books the level itself; a point already past it (a gapped open) books
264/// that print. Under a quantizing trigger grid the core re-validates the
265/// activation on the same ladder the matcher used, so a hit the matcher reports is
266/// never refused.
267struct Stop {
268 double price = 0.0;
269};
270/// A stop that, once reached, becomes a limit at `limit`. The stop gates and the
271/// limit bounds; an anchored FromOwnerFill level is refused for this trigger,
272/// because the anchor materializes one level and this trigger has two.
273struct StopLimit {
274 double stop = 0.0;
275 double limit = 0.0;
276};
277/// Trail offset spelled in price ticks instead of a price distance. The
278/// acceptance path resolves it against the run's price tick, writes the
279/// product into Trail::offset and clears the spelling, so a stored definition
280/// always carries a plain price distance and can never resolve twice.
282 double ticks = 0.0;
283};
284/// offset is a price distance: the stop rides `offset` behind the running
285/// best. Zero is legal and means "ride the best": the exit is the first
286/// adverse move past it. Negative and nonfinite offsets are rejected.
287/// arm_price absent means the trail is already armed and starts riding at the
288/// first print. Under a FromOwnerFill anchor it means the owner's fill
289/// supplies the threshold, which is that anchor's whole point: absent is the
290/// anchored spelling, 0.0 is the equivalent placeholder kept legal for hosts
291/// that already write it, and any other written level is refused because the
292/// arm would overwrite it.
293///
294/// best_seed is where the running best STARTS, appended last so every existing
295/// {offset}, {offset, arm_price} and {offset, arm_price, ticks} initializer
296/// keeps its meaning. Absent (the default) is the established behaviour: the
297/// best is the arm's own print -- the arm threshold's own ladder point when a
298/// crossing armed it, the first print the trail sees when it was submitted
299/// already armed. A present seed is a floor on that start: at the arm the best
300/// becomes the favourable one of the seed and the arm print (the higher for a
301/// sell trail, the lower for a buy trail), so a host whose position already
302/// reached a level before this request existed does not restart the ride at a
303/// worse print. It is a generic broker shape -- a trail that rides from its
304/// activation rather than from the next print -- and the level is the host's
305/// own number: the kernel neither derives it nor knows what named it. It is
306/// absolute (an anchor moves the arm threshold, never the seed), it must be
307/// finite and positive, and under a price grid it is put on the ladder exactly
308/// like an observed print. It is folded into the request digest only when
309/// present, so every trail without one keeps the digest it had before the
310/// field existed.
311struct Trail {
312 double offset = 0.0;
313 std::optional<double> arm_price;
314 std::optional<TrailTicks> ticks = std::nullopt;
315 std::optional<double> best_seed = std::nullopt;
316};
317using Trigger = std::variant<Market, Limit, Stop, StopLimit, Trail>;
318
319/// How a materialized anchored level is snapped onto the run's price tick
320/// ladder (NativeRunSpec::price_tick). Raw keeps `fill + offset` exactly,
321/// which is the established behaviour. HalfUp is the nearest tick with ties
322/// away from zero. Directional rounds toward the price region the resting leg
323/// needs, relative to the leg's trigger kind and side exactly as
324/// NativeGridRounding documents it: a buy limit down and a sell limit up, a
325/// stop the other way; a trail arm threshold is reached from the favourable
326/// side like a limit and rounds like one. Either rounding needs a positive
327/// price tick, checked at acceptance exactly like a tick-spelled offset. This
328/// is a generic instrument grid: a source language's own trigger projection
329/// is not spelled here (a host restates the level through its arm hook).
330enum class NativeAnchorRounding : std::uint8_t {
331 Raw = 0,
334};
335
336/// Where a request's trigger level comes from. Absolute is the level written
337/// in the trigger itself. FromOwnerFill defers it to the owner's fill: the
338/// level becomes fill + offset when the owner arms the request, so a bracket
339/// leg can be placed before its parent has a price. offset is signed (adverse
340/// is negative) and is spelled in price ticks when `ticks` is set; acceptance
341/// resolves a tick spelling exactly like TrailTicks, in place, once. The
342/// materialized level is then snapped per `rounding`, which is appended last
343/// so every existing aggregate initializer keeps its meaning.
344struct Absolute {};
345/// Defer a level to the owner's fill: at the arm the level becomes fill + offset,
346/// with `offset` signed (adverse is negative) and spelled in price ticks when
347/// `ticks` is set, then rounded by `rounding`. Legal on Limit and Stop prices and
348/// on a Trail arm threshold, and only under a WaitForApplied owner, which is the
349/// one relation that arms. Until then the anchored field is left unwritten (0.0
350/// for a Limit or Stop price, an absent or 0.0 Trail::arm_price), and the ArmedEvent
351/// carries the materialized definition: from then on the request reads as that level.
357using TriggerAnchor = std::variant<Absolute, FromOwnerFill>;
358
359/// Replacement behaviour that is not expressible in the successor request.
360/// retain_trigger_state carries the predecessor's live trigger state (a
361/// tracking trail's best, an already active stop) into the successor instead
362/// of restarting it; predecessor and successor must hold the same trigger
363/// alternative.
364///
365/// keep_handle re-prices the request in place: the new definition keeps the
366/// target's handle, and with it the request's lineage (its predecessor link
367/// and chain root) and its dependents -- a child waiting on it keeps waiting,
368/// a roster that names it keeps it -- where a plain replace retires the handle
369/// and issues a successor. Everything else is the replace: the same checks and
370/// refusals, a new immutable definition and birth, a fresh live row (remaining,
371/// allowance and trigger state restart unless retain_trigger_state keeps the
372/// last), and one ReplacedEvent, whose two definitions share the handle. The
373/// re-price takes the number of the run's request sequence a successor's
374/// incarnation would have taken, as the definition's priority
375/// (RequestDefinition::priority): matching ranks it the newest request, where a
376/// successor stands, and the requests after it are numbered as if the replace
377/// had issued a handle. No handle is issued under that number.
378///
379/// keep_binding carries a close's book binding across the replace. A successor
380/// that starts unbound (an Independent close) and whose predecessor was bound
381/// to the book, or carried such a binding itself, records the binding's cycle
382/// and side (RequestDefinition::kept_binding). Where the successor would bind,
383/// at its first evaluation, a book that still has that cycle and side binds it
384/// with no CloseBoundEvent; any other book binds it, or ends it, exactly as it
385/// does a successor that carried nothing. So the option changes the events a
386/// run records, never what it matches. Anywhere else it carries nothing.
387/// Either option may be given alone.
390 bool keep_handle = false;
391 bool keep_binding = false;
392};
393
394/// Take whatever the point allows, with no per-point cap. The default capacity,
395/// and the only one the Pine adapter emits.
397/// Cap what this request may execute at ONE matching point at `units`. Fixed at
398/// submit and metered per point — including after an arm — so a request cannot be
399/// drained faster than its owner's schedule allows.
401 double units = 0.0;
402};
403using Capacity = std::variant<ImmediateRemaining, PointBudget>;
404
405/// Whether an owner-related request that arms is a working order before its
406/// arm. Working (the default) is the established book: the request is listed
407/// by native_working_requests() from acceptance on. PendingUntilArmed keeps
408/// it out of that enumeration until its ArmedEvent; it is still a live
409/// request the whole time -- accepted, addressable by handle (replace,
410/// cancel, trail_state), counted by cancel_all / cancel_where, and folded
411/// into the continuation identity -- and it never matches before the arm
412/// under either value. Visibility governs enumeration, not addressing.
413enum class NativeArmVisibility : std::uint8_t {
416};
417
418/// When an armed request may first match. The arm happens inside its owner's
419/// fill settlement, at the owner's fill print. AtArmPrint (the default) is the
420/// established book: the armed request is a candidate at that very cursor, so
421/// a level the fill print already satisfies matches at the print. AfterArmPrint
422/// gives the armed request the birth rule of a request submitted from the
423/// owner's fill callback: on the driver point that armed it, it sees only the
424/// path after the print -- a level the print already satisfies does not match
425/// there, a later crossing on that point still does -- and from the next
426/// driver point on it is an ordinary working request. It governs the level
427/// test of a priced trigger (limit, stop, stop-limit, trail arm); a market
428/// trigger has no level and is unaffected. Both are broker models: a
429/// contingent child that enters the book after its parent's trade cannot
430/// trade on that print, a simulated bracket commonly may.
431enum class NativeArmFirstMatch : std::uint8_t {
434};
435
436/// What an armed CLOSING request (Reduce, Flatten, a HostSized close) closes.
437/// OwnerLot (the default) is the established relation: the lot its owner's
438/// fill opened, and nothing a later add brings. Book binds it, at the arm, to
439/// the whole position that fill left -- the very book authority an
440/// Independent close submitted from the owner's fill callback acquires -- so
441/// a protective leg placed with its entry covers later adds and settles
442/// through the book like any other close. A HostSized close may wait for its
443/// owner only under Book: the owner-lot relation sizes from the units the
444/// owner opened, whereas a book close is sized by the host at the match.
445/// A waiting transaction closes nothing, so it must keep OwnerLot.
446enum class NativeArmScope : std::uint8_t {
448 Book = 1,
449};
450
451/// No owner: the request stands on its own book authority. Required for Sized, and
452/// the default.
453struct Independent {};
454/// The one owner relation that arms (the ArmedEvent). `visibility`, then
455/// `first_match`, then `scope` are appended last so every existing {parent},
456/// {parent, visibility} initializer keeps its meaning.
463/// Bind to ONE already-live opening, by its handle and the positive cycle it is
464/// live in. A reduce bound this way closes that lot and nothing a later add
465/// brings.
468 int64_t cycle = 0;
469};
470/// Immutable membership, independent of a source scratch scope. A successful
471/// read changes out; an error leaves it unchanged. No read allocates.
473public:
474 virtual ~OpeningReadback() noexcept = default;
475 virtual std::uint64_t count() const noexcept = 0;
476 virtual detail::ScratchStatus opening_at(
477 std::uint64_t index, RequestHandle& out) const noexcept = 0;
478};
479
480/// Call-local input. Enrollment freezes it before retaining a definition;
481/// neither this object nor its callback/context is retained.
483public:
484 using Reader = detail::ScratchStatus (*)(void*, std::uint64_t, RequestHandle&);
485 OpeningInputCursor(std::uint64_t count, void* context, Reader reader) noexcept
486 : count_(count), context_(context), reader_(reader) {}
487 std::uint64_t count() const noexcept override { return count_; }
489 std::uint64_t index, RequestHandle& out) const noexcept override;
490private:
491 std::uint64_t count_;
492 void* context_;
493 Reader reader_;
494};
495
496/// Freeze and canonicalize a same-run, unique stream. Uses bounded workspace
497/// and an exclusive unlinked file; failure leaves out unchanged (an allocation
498/// failure throws typed out_of_memory). Only creation of the fixed-size owner allocates.
500 const RunIdentity&, const OpeningReadback&,
501 std::shared_ptr<const OpeningReadback>& out);
502
503std::uint64_t opening_count(const std::vector<RequestHandle>&,
504 const OpeningReadback*) noexcept;
505detail::ScratchStatus opening_at(const std::vector<RequestHandle>&,
506 const OpeningReadback*, std::uint64_t, RequestHandle&) noexcept;
507/// Bind to a fixed cohort of already-live openings. Every handle must be unique,
508/// nonzero, of this run and physically live in `cycle`; enrollment rejects the
509/// ENTIRE request if any member is invalid. The accepted definition keeps the
510/// original cohort in canonical handle order even as later retirement narrows the
511/// live subset, and execution still closes lots in physical FIFO order.
513 std::vector<RequestHandle> openings;
514 int64_t cycle = 0;
515 std::shared_ptr<const OpeningReadback> external_openings;
516};
517/// Bind to a host-built roster (cohort_open / cohort_add / cohort_remove), read at
518/// the match rather than frozen at submit. The C surface pairs it with exactly one
519/// intent, a host-sized close, whose units the on_close_units hook answers.
523using Owner = std::variant<Independent, WaitForApplied, BindOpening, BindOpenings, BindCohort>;
524
525enum class GroupEffect : std::uint8_t { Cancel = 0, Reduce = 1 };
526struct NoGroup {};
527struct Member {
528 std::uint64_t group = 0;
529 std::int64_t cohort = 0;
531};
532using Group = std::variant<NoGroup, Member>;
533
534/// Aggregate field order keeps market construction:
535/// Request{Transact{1.0}, "buy", "comment"}
536/// Request{Flatten{}, "flat", ""}
537/// Request{Reduce{ExplicitUnits{3}}, "close", ""}
538/// New fields default to Market / ImmediateRemaining / Independent / NoGroup.
539/// market_request() converts a physical execution::Action (explicit Reduce
540/// units only) into that same market Request. Host market-only methods must
541/// reject nondefault extras; they must not drop them.
552
553/// Complete request schema, including owner text and every opening handle.
554/// intent/trigger/capacity/owner/group/anchor have an unsigned variant `tag`;
555/// label/comment are top-level byte fields. See the production freeze codec.
557public:
558 RequestReadback() noexcept = default;
559 RequestReadback(const detail::CommandScratchStore& store, detail::ScratchHandle version) noexcept
560 : PayloadReadback(store, version, detail::PayloadSchema::request) {}
561 explicit RequestReadback(detail::PayloadReadback view) noexcept : PayloadReadback(view) {}
563 return find("label", out);
564 }
566 return find("comment", out);
567 }
568};
569
571 if (std::holds_alternative<execution::Flatten>(action)) return Flatten{};
572 if (const auto* transact = std::get_if<order_action::Transact>(&action)) {
573 return Transact{transact->signed_units};
574 }
575 const auto& reduce = std::get<order_action::Reduce>(action);
576 return Reduce{ExplicitUnits{reduce.units}};
577}
578
580 std::string label = {},
581 std::string comment = {}) {
582 return Request{intent_from_physical(action), std::move(label), std::move(comment)};
583}
584
585inline bool has_market_defaults(const Request& request) noexcept {
586 return std::holds_alternative<Market>(request.trigger)
587 && std::holds_alternative<ImmediateRemaining>(request.capacity)
588 && std::holds_alternative<Independent>(request.owner)
589 && std::holds_alternative<NoGroup>(request.group)
590 && std::holds_alternative<Absolute>(request.anchor);
591}
592
593struct EventId {
595 uint64_t ordinal = 0;
596};
597
598inline bool operator==(const EventId& a, const EventId& b) {
599 return a.ordinal == b.ordinal && a.run == b.run;
600}
601inline bool operator!=(const EventId& a, const EventId& b) { return !(a == b); }
602
605 double t = 0.0;
606};
607
608inline bool operator==(const MatchCursor& a, const MatchCursor& b) noexcept {
609 return a.point.ordinal == b.point.ordinal && a.t == b.t
610 && a.point.interval_index == b.point.interval_index
611 && a.point.effective_time_ms == b.point.effective_time_ms
612 && a.point.provenance == b.point.provenance
613 && a.point.path_phase == b.point.path_phase;
614}
615inline bool operator!=(const MatchCursor& a, const MatchCursor& b) noexcept { return !(a == b); }
616
617inline std::uint8_t display_waypoint(const MatchCursor& cursor) noexcept {
618 return static_cast<std::uint8_t>(cursor.point.path_phase);
619}
620
623/// A resolved remaining quantity, in units. The kernel publishes a Sized intent's
624/// own resolution here, BEFORE the host's terms hook runs, so an override sees the
625/// kernel's number and may answer with a different one.
627 double q = 0.0;
628};
630/// A dynamic cohort has no currently live member. This is a live deferral,
631/// not a terminal receipt: the consumer retries it at the next candidate.
632struct NoTarget {};
635
639 double q = 0.0;
640};
647
649/// The authority an unarmed WaitForApplied child holds: accepted and live, but
650/// never a matching candidate until its owner's fill arms it — under either
651/// visibility.
661/// The authority an armed leg acquires under NativeArmScope::Book: the whole
662/// position its owner's fill left, on this cycle and side, bound at the fill's
663/// cursor. It is the same authority an Independent close has, which is what lets a
664/// protective leg placed with its entry also cover later adds.
678using Enrollment = std::variant<EnrollmentFromCommand, EnrollmentFromApplied>;
685/// Immutable fixed cohort. Current physical liveness is observed, never stored
686/// here; later fragments of an enrolled provenance remain authorized.
688 std::vector<RequestHandle> openings;
689 int64_t cycle = 0;
692 std::shared_ptr<const OpeningReadback> external_openings;
693};
699
700/// Native authorization receipt, converted to a call-local financial
701/// SelectedOpeningSet only at the consumer's settlement boundary.
703 int64_t cycle = 0;
704 std::vector<uint64_t> incarnations;
705};
706/// What one execution acted on: the whole book, one opening's exposure, or a bound
707/// selection. Reported on ExecutionAppliedEvent::scope. The financial
708/// execution::CloseScope remains the original two-alternative type; this is the
709/// native view, and a selected close's committed row range identifies the actual
710/// contributors.
711using ExecutionScope = std::variant<execution::Book, execution::OpeningExposure, SelectedExposure>;
712
713struct MarketReady {};
714struct LimitReady {};
715struct StopIdle {};
716struct StopActive {};
719struct TrailWaitArm {};
720/// A trail riding its running best. `activation_ordinal` is the ordinal of
721/// this request's own TrailArm ActivatedEvent -- NativeTrailState's
722/// activation_ordinal, kept in state so it outlives the journal window -- and
723/// 0 for a successor that retained its predecessor's tracking
724/// (ReplaceOptions::retain_trigger_state), which never armed itself. A
725/// re-price that kept the handle (ReplaceOptions::keep_handle) keeps it: that
726/// same request armed.
728 double best = 0.0;
729 uint64_t activation_ordinal = 0;
730};
731/// A triggered trail; `activation_ordinal` carries TrailTrack's.
733 double best_at_trigger = 0.0;
734 uint64_t activation_ordinal = 0;
735};
738
741 uint64_t point_ordinal = 0;
742 double initial = 0.0;
743 double left = 0.0;
744};
746 uint64_t point_ordinal = 0;
747};
749 uint64_t point_ordinal = 0;
750};
753
754struct PendingNone {};
756 double total = 0.0;
757 uint64_t count = 0;
759};
760using PendingAdjustments = std::variant<PendingNone, PendingDeferred>;
761
762struct PositionFlat {};
764 int64_t cycle = 0;
766};
767using PositionIdentity = std::variant<PositionFlat, PositionNonflat>;
768
775
776/// Borrowed through the enclosing command/evaluation and any prepare/install
777/// pair that copies its TargetObservation, including nested reads.
779public:
780 virtual ~OpeningObservationReadback() noexcept = default;
781 virtual std::uint64_t count() const noexcept = 0;
782 virtual detail::ScratchStatus observation_at(
783 std::uint64_t index, OpeningObservation& out) const noexcept = 0;
784};
785
788 std::optional<OpeningObservation> opening;
789 std::vector<OpeningObservation> openings;
791};
792
793std::uint64_t opening_observation_count(const TargetObservation&) noexcept;
795 const TargetObservation&, std::uint64_t, OpeningObservation&) noexcept;
796
797/// Who authored a request. Host is every request a host submits, replaces or
798/// cancels, and it is the whole existing population: it folds nothing into the
799/// continuation digest, so no established hash moves. KernelLiquidation marks
800/// the margin model's own Reduce or Flatten; KernelRisk marks the Flatten a
801/// NativeRiskAction::FlattenAndBlock breach issues at its breach point
802/// (native_execution_consumer.cpp, pinned by tests/test_native_risk_limits.cpp).
803enum class RequestOrigin : std::uint8_t {
804 Host = 0,
807};
808
809/// The immutable accepted form of one request: its handle, the request value as
810/// accepted, its birth (acceptance ordinal and decision floor), its origin
811/// (RequestOrigin::Host, or the kernel's own for a liquidation or a risk flatten)
812/// and its authority. Every event carries a shared pointer to it, so a reader
813/// holds the definition the kernel matched against and not a later re-read.
818 std::optional<RequestHandle> predecessor;
819 /// Appended last so every existing aggregate initializer keeps its meaning.
821 /// The first handle of this request's replace chain -- the request the
822 /// chain began with, which is how a cohort roster names every request of
823 /// the chain (cohort_add, cohort_contains). Empty for a request that
824 /// replaced nothing: its chain begins with itself. Set by the core at the
825 /// replace that creates the definition, from the predecessor's own root,
826 /// so it never needs the predecessor's journal events. Appended after
827 /// `origin` for the same reason `origin` was.
828 std::optional<RequestHandle> root;
829 /// Where the definition stands in the run's queue: the number of the
830 /// run's request sequence the command that placed it took. Matching
831 /// breaks a tie at one cursor oldest number first, and the working book
832 /// is held in this order (WorkingRequestCore::live). 0, as every accepted
833 /// request and replace successor has it, means the handle's incarnation,
834 /// the number its command took; a re-price that kept the handle
835 /// (ReplaceOptions::keep_handle) sets the number it took instead. Read it
836 /// through LiveRequest::priority. Appended after `root`, like it.
837 uint64_t priority = 0;
838 /// The book binding a replace carried forward (ReplaceOptions::
839 /// keep_binding): the cycle and side of the book its predecessor was bound
840 /// to. The request starts unbound, and at its first evaluation a live book
841 /// with this cycle and side binds it with no CloseBoundEvent. Empty for
842 /// every other definition.
843 std::optional<PositionNonflat> kept_binding;
844};
845using DefinitionRef = std::shared_ptr<const RequestDefinition>;
846
854 /// Placement-time sizing measurements frozen from the accepting command
855 /// context. Both stay empty for every request that did not ask to freeze
856 /// one, and an empty optional folds nothing into the continuation digest.
857 std::optional<double> sizing_units; // SizeTime::AtAcceptance
858 std::optional<double> sizing_scope; // ScopeBasis::AtAcceptance
859 std::optional<double> sizing_price; // SizePrice::Signal / SignalOnTick
860
861 const RequestHandle& handle() const noexcept { return definition->handle; }
862 const Request& request() const noexcept { return definition->request; }
863 const Birth& birth() const noexcept { return definition->birth; }
864 const std::optional<RequestHandle>& predecessor() const noexcept {
865 return definition->predecessor;
866 }
867 /// Its place in the queue (RequestDefinition::priority): the handle's
868 /// incarnation, unless a re-price kept the handle and took a later number.
869 uint64_t priority() const noexcept {
870 return definition->priority != 0 ? definition->priority : definition->handle.incarnation;
871 }
872};
873
874/// Whether a request may be considered at this driver point: the point's ordinal
875/// must be strictly past the birth acceptance ordinal AND its effective time at or
876/// past the birth's decision-time lower bound. This is the birth gate — it is why
877/// a request accepted on bar N cannot fill on that bar's already delivered
878/// opening — and a request born mid-path in on_native_applied is admitted at the
879/// current cursor instead, on the unconsumed suffix of that segment.
880inline bool point_eligible(const LiveRequest& live,
881 uint64_t point_ordinal,
882 int64_t effective_time_ms) noexcept {
883 return point_eligible(live.birth(), point_ordinal, effective_time_ms);
884}
885
886/// v2 exact binary64 grid: r=abs(q)/s, n=round(r) half away from zero, g=n*s.
887/// Intermediates finite, 1<=n<=2^53, abs(abs(q)-g) <= 4*ulp(max(abs(q),abs(g)))
888/// and < s/2. ulp is nextafter toward +inf. Does not rewrite q.
889inline bool quantity_on_grid(double q, double step) noexcept {
890 if (!std::isfinite(q) || !std::isfinite(step) || step <= 0.0) return false;
891 const double abs_q = std::abs(q);
892 const double r = abs_q / step;
893 if (!std::isfinite(r)) return false;
894 const double n = std::round(r);
895 if (!std::isfinite(n) || n < 1.0 || n > 0x1p53) return false;
896 const double g = n * step;
897 if (!std::isfinite(g)) return false;
898 const double err = std::abs(abs_q - g);
899 const double span = std::max(abs_q, std::abs(g));
900 const double ulp = std::nextafter(span, std::numeric_limits<double>::infinity()) - span;
901 const double ulp_bound = 4.0 * ulp;
902 const double half_step = step / 2.0;
903 if (!std::isfinite(err) || !std::isfinite(ulp_bound) || !std::isfinite(half_step))
904 return false;
905 return err <= ulp_bound && err < half_step;
906}
907
908/// Why an acceptance was refused, on a Rejected SubmitResult. These are decided at
909/// submit, before any request record exists: an invalid quantity or basis, an
910/// off-grid quantity, an owner the intent may not take, a trigger spelling the run
911/// cannot resolve, or a frozen quantity the run's own opening admission refuses
912/// (PlacementAdmission).
913enum class RequestRejectReason : std::uint8_t {
921 /// A Sized{SizeTime::AtAcceptance} whose acceptance-resolved quantity does
922 /// not pass the run's opening admission (allowed directions, max_abs_units,
923 /// max_open_lots, initial margin) at the sizing price.
925};
926
930
931/// What submit answers. Accepted carries a timeline ordinal and a RequestHandle
932/// (session, run, incarnation); Rejected carries a rejection ordinal, no handle
933/// and a reason. Acceptance is not a fill: no lot, no cash and no fee moves here,
934/// and the ordinary fill appears later as an ExecutionAppliedEvent on the same
935/// command history.
938 uint64_t event_ordinal = 0;
939 std::optional<RequestHandle> handle;
940 std::optional<RequestRejectReason> reason;
941};
942
945 uint64_t event_ordinal = 0;
946 std::optional<RequestHandle> successor;
947 std::optional<RequestRejectReason> reason;
948};
949
954
955/// Why a CANDIDATE was refused at a matching point. Every refusal is terminal:
956/// the MatchRejectedEvent ends the request, and a host that still wants it
957/// submits again. OpeningDirection, MaxAbsUnits, MaxOpenLots, InitialMargin and
958/// RiskLimit are the run's admission gates; TermsUnresolved is a host-sized or
959/// basis-sized quantity that could not be resolved; NoOppositeExposure and
960/// InvalidTerms are shapes the answered terms cannot take;
961/// UnrepresentableQuantity is a quantity the settlement cannot book exactly on
962/// the book it met.
963enum class MatchRejectReason : std::uint8_t {
973 /// An opening refused while the run spec's generic risk limits are
974 /// blocking (L9). Reduces never reach this gate.
976 /// The settlement's inspection answered execution::Status::
977 /// UnrepresentableQuantity: a close split binary64 cannot hold (a rest
978 /// below half an ulp of the lot it ends in, a whole lot that cannot
979 /// decrement the rest or move the running sum), a reduction or transaction
980 /// the position absorbs, or an opening the surviving book absorbs; or the
981 /// request core cannot take the fill off the request's own units
982 /// (CoreFailure::NonrepresentableQuantity: a scope of dust closed by a far
983 /// larger request, a point budget too small to move its remaining units);
984 /// or what an off-grid Reduce the grid admitted as a FIFO boundary of its
985 /// scope settles no longer closes whole lots of the book it meets
986 /// (CommandContext::units_are_scope_boundary). The request ends here,
987 /// nothing moves, and the run goes on; the settlement's and the core's
988 /// cases stopped the whole run before R5 lane K-ULP4.
990};
991
992/// Which price a candidate is being offered at: the point's own price, or the
993/// request's trigger level (a crossing that books the level itself). A host reads
994/// it off the terms facts, and the trail suites read it back off the applied event
995/// to tell a level fill from a path fill.
996enum class NativeCandidatePriceKind : std::uint8_t {
1000};
1001
1002enum class OpeningShape : std::uint8_t {
1006};
1007
1014
1015/// The settlement shape one execution takes. Derived by the kernel from the intent
1016/// and the answered terms; a host never constructs it.
1019
1021 return std::visit([](const auto& alternative) -> ExecutionPlan {
1022 return alternative;
1023 }, action);
1024}
1025
1033
1034/// Why a request left the book. Host is an explicit cancel; Group an OCA sibling's
1035/// effect; OwnerGone a parent that was rejected, replaced or cancelled, which ends
1036/// its waiting children; Superseded the kernel withdrawing its own liquidation
1037/// before re-pricing it. Every reason is carried on a CancelledEvent.
1038enum class CancelReason : std::uint8_t {
1039 User = 0,
1043 /// A kernel-originated request the kernel itself withdrew: the liquidation
1044 /// level or its units moved, or the requirement is no longer breached.
1046};
1047
1053
1054enum class ActivationKind : std::uint8_t {
1055 Stop = 0,
1059};
1060
1061enum class CoreFailure : std::uint8_t {
1069 /// A group deduction's pending total that overflows binary64 (or a
1070 /// non-finite reservation, which a valid run does not reach). A deduction
1071 /// binary64 cannot take off its recipient is absorbed instead (R5 lane
1072 /// K-ULP5); it failed the run with this discriminator before.
1077};
1078
1088
1089enum class CommandSurface : std::uint8_t { General = 0, MarketOnly = 1 };
1090
1091/// One request was accepted: the definition, and the timeline ordinal it was
1092/// accepted at. The first event of every request's history.
1094 uint64_t ordinal = 0;
1097 const RequestHandle& handle() const noexcept { return definition->handle; }
1098 const Request& request() const noexcept { return definition->request; }
1099 const Birth& birth() const noexcept { return definition->birth; }
1100};
1101
1108
1110 uint64_t ordinal = 0;
1114 const RequestHandle& predecessor() const noexcept { return predecessor_definition->handle; }
1115 const Request& predecessor_request() const noexcept { return predecessor_definition->request; }
1116 const RequestHandle& successor() const noexcept { return successor_definition->handle; }
1117 const Request& successor_request() const noexcept { return successor_definition->request; }
1118 const Birth& successor_birth() const noexcept { return successor_definition->birth; }
1119 /// Whether the replace kept the handle (ReplaceOptions::keep_handle): the
1120 /// successor definition is the same request re-priced, and only such a
1121 /// definition carries a priority of its own.
1122 bool kept_handle() const noexcept { return successor_definition->priority != 0; }
1123};
1124
1134
1146
1153
1160
1161/// A reduction or a flatten that had nothing to close: terminal, with no execution
1162/// identity, no fill, no fee and no physical action. It is an outcome, not a
1163/// failure.
1165 uint64_t ordinal = 0;
1170 const RequestHandle& handle() const noexcept { return definition->handle; }
1171 const Request& request() const noexcept { return definition->request; }
1172 const Birth& birth() const noexcept { return definition->birth; }
1173};
1174
1175/// One candidate's execution terms, recorded. For a request whose units were
1176/// deferred it also binds them: it spends the pending group chain
1177/// (`prior_adjustment_ids`, `pending_total`), takes `effective_deduction` off the
1178/// resolved units and leaves `remaining_after`. That deduction is 0 with a
1179/// positive `pending_total` and positive resolved units when binary64 cannot
1180/// take the total off them -- absorbed, and the units stand (R5 lane K-ULP5).
1196
1197/// A candidate the run refused, with its MatchRejectReason and the cursor it was
1198/// refused at. It carries an event ordinal but no execution identity, and it is
1199/// the request's last event: every match refusal erases the live request.
1212
1222
1232
1233/// One committed execution: the definition, the cursor, the resolved price and
1234/// units, the scope it acted on, the committed closed-row range, the cycle on
1235/// either side, and the ticket it booked under. Delivered to on_native_applied and
1236/// recorded in native_events(); the account observation that follows it shares its
1237/// ordinal.
1239 uint64_t ordinal = 0;
1241 double raw_price = 0.0;
1242 double resolved_price = 0.0;
1243 double current_ticket = 0.0;
1244 std::size_t first_trade_index = 0;
1245 std::size_t closed_trade_count = 0;
1247 double closed_units = 0.0;
1248 double opened_units = 0.0;
1249 double filled_working = 0.0;
1254 bool terminal = true;
1255 std::optional<AppliedTerminalReason> terminal_reason;
1256 int64_t cycle_before = 0;
1257 int64_t cycle_after = 0;
1260 const RequestHandle& handle() const noexcept { return definition->handle; }
1261 const Request& request() const noexcept { return definition->request; }
1262 const Birth& birth() const noexcept { return definition->birth; }
1263 int64_t effective_time_ms() const noexcept { return cursor.point.effective_time_ms; }
1264 int64_t interval_open_ms() const noexcept { return cursor.point.open_ms; }
1265 int64_t interval_last_traded_close_ms() const noexcept {
1266 return cursor.point.last_traded_close_ms;
1267 }
1268 int interval_index() const noexcept { return cursor.point.interval_index; }
1269 std::uint8_t provenance() const noexcept {
1270 return static_cast<std::uint8_t>(cursor.point.provenance);
1271 }
1272};
1273
1274/// A group effect's deduction from a live sibling whose remaining units are
1275/// known (GroupEffect::Reduce): `requested_delta` is the member fill's
1276/// `filled_working`, `actual_deduction` the part taken -- capped at `before`,
1277/// which then cancels the sibling -- and `after` is fl(before -
1278/// actual_deduction). A deduction binary64 cannot take off `before` (at most
1279/// half an ulp of it: fl(before - d) is `before`) is absorbed (R5 lane K-ULP5):
1280/// `actual_deduction` is 0 and `after` equals `before`, the exact binary64
1281/// result, and the member's fill stands. A deduction that is taken is positive,
1282/// so an `actual_deduction` of 0 names an absorbed one.
1294
1295/// A group effect's deduction deferred into a sibling whose units are not known
1296/// yet: its pending total grows by `deferred_delta`, in commit order, and the
1297/// receipts chain back through `previous_pending_receipt` until the terms or
1298/// the owner fill that binds the sibling's units takes the total off them. A
1299/// fill the total cannot move (fl(total + fill) is the total) is absorbed (R5
1300/// lane K-ULP5): `deferred_delta` is 0, `pending_after` is `pending_before` and
1301/// the event joins no chain. A total below half an ulp of a later fill rounds
1302/// into it, as binary64 addition does.
1314
1315/// A Reduce{OwnerOpenedUnits} was bound, at its owner's fill, to what that fill
1316/// opened. It is how a bracket child's size becomes a number, and it precedes the
1317/// ArmedEvent of the same drain. `effective_deduction` is the part of the child's
1318/// pending group total taken off `source_units`; it is 0 with a positive
1319/// `pending_total` when binary64 cannot take that total off them -- absorbed, and
1320/// the owner's units stand (R5 lane K-ULP5).
1331
1332/// An anchored leg was materialized, exactly once: fill + offset, then the
1333/// rounding, then resolve_anchored_level, then the representability check, and the
1334/// resulting level written into the leg's trigger with its anchor becoming
1335/// Absolute. Every later reader — the live book, the C working rows, matching —
1336/// sees that level. Under NativeArmScope::Book it also carries the BookClose the
1337/// leg acquired.
1346
1347/// A kernel-issued liquidation that actually filled. It carries the margin
1348/// facts of that fill, so a host reconstructs the outcome without recomputing
1349/// the account: `mark` is the booked resolved price, `equity` and `required`
1350/// are the marked equity and the maintenance requirement of the SURVIVING book
1351/// at that price, `liquidation_price` is the level re-solved for what is left,
1352/// and `position_before` / `position_after` are the signed book on either side
1353/// of the reduction. `applied` names the ExecutionAppliedEvent that booked it.
1355 uint64_t ordinal = 0;
1360 double mark = 0.0;
1361 double equity = 0.0;
1362 double required = 0.0;
1363 double liquidation_price = 0.0;
1364 double units = 0.0;
1365 double position_before = 0.0;
1366 double position_after = 0.0;
1367 const RequestHandle& handle() const noexcept { return definition->handle; }
1368 const Request& request() const noexcept { return definition->request; }
1369};
1370
1371/// Which generic risk limit of NativeRunSpec::risk a NativeRiskEvent reports.
1378
1379/// One generic risk limit breaching (L9). It is not bound to a request: the
1380/// block it opens is an account fact, so this event carries no definition.
1381/// `limit` is the threshold in the unit the breach was measured in — account
1382/// currency for the two loss limits (a percent limit is already resolved
1383/// against its basis equity here), days or fills for the two counts — and
1384/// `observed` is the measured value that reached it. `day_ordinal` is the
1385/// risk day the breach happened on, on the spec's own day basis, and `cursor`
1386/// is the point it was measured at.
1395
1396/// Every alternative of one request's history, as one value. native_events()
1397/// returns them as the NativeEventKind::Command rows, and
1398/// strategy_native_events_v1 flattens the same set into one tagged POD. The
1399/// variant's size is pinned by the frozen C++ ABI fixtures.
1400using CommandEvent = std::variant<AcceptedEvent,
1415 ArmedEvent,
1419
1421public:
1422 explicit JournalRecordSource(const CommandEvent&) noexcept;
1423 std::uint64_t size() const noexcept override { return length_; }
1424 detail::ScratchStatus read(std::uint64_t, detail::ScratchBuffer) const noexcept override;
1425private:
1426 const CommandEvent* event_;
1427 std::uint64_t length_ = 0;
1429};
1431// Owning decode is an explicit diagnostic surface; an allocation failure throws typed out_of_memory.
1434class WorkingRequestCore;
1437 const detail::ScratchByteSource&) noexcept;
1439
1440/// Almost every prepared command yields one history event. Keep that ordinary
1441/// transactional payload inline; the overflow vector preserves the existing
1442/// arbitrary-length behavior for group/lifecycle plans that emit more events.
1444public:
1446 InlineCommandEvents(InlineCommandEvents&& other) noexcept : size_(other.size_) {
1447 for (std::size_t i = 0; i < size_ && i < inline_.size(); ++i) {
1448 inline_[i] = std::move(other.inline_[i]);
1449 }
1450 overflow_ = std::move(other.overflow_);
1451 other.size_ = 0;
1452 }
1454 if (this != &other) {
1455 clear();
1456 size_ = other.size_;
1457 for (std::size_t i = 0; i < size_ && i < inline_.size(); ++i) {
1458 inline_[i] = std::move(other.inline_[i]);
1459 }
1460 overflow_ = std::move(other.overflow_);
1461 other.size_ = 0;
1462 }
1463 return *this;
1464 }
1465 InlineCommandEvents(const InlineCommandEvents& other) : size_(other.size_) {
1466 for (std::size_t i = 0; i < size_ && i < inline_.size(); ++i) {
1467 inline_[i] = other.inline_[i];
1468 }
1469 overflow_ = other.overflow_;
1470 }
1472 if (this != &other) {
1473 clear();
1474 size_ = other.size_;
1475 for (std::size_t i = 0; i < size_ && i < inline_.size(); ++i) {
1476 inline_[i] = other.inline_[i];
1477 }
1478 overflow_ = other.overflow_;
1479 }
1480 return *this;
1481 }
1482
1483 std::size_t size() const noexcept { return size_; }
1484 bool empty() const noexcept { return size_ == 0; }
1485
1486 template<class Event>
1487 void emplace_back(Event&& event) {
1488 push_back(CommandEvent(std::forward<Event>(event)));
1489 }
1490
1492 if (overflow_.empty() && size_ < inline_.size()) {
1493 inline_[size_++].emplace(std::move(event));
1494 return;
1495 }
1496 if (overflow_.empty()) {
1497 overflow_.reserve(inline_.size() * 2U);
1498 for (std::size_t i = 0; i < size_; ++i) {
1499 overflow_.push_back(std::move(*inline_[i]));
1500 inline_[i].reset();
1501 }
1502 }
1503 overflow_.push_back(std::move(event));
1504 ++size_;
1505 }
1506
1507 void clear() noexcept {
1508 for (std::size_t i = 0; i < size_ && i < inline_.size(); ++i) {
1509 inline_[i].reset();
1510 }
1511 overflow_.clear();
1512 size_ = 0;
1513 }
1514
1515 CommandEvent& front() noexcept { return (*this)[0]; }
1516 const CommandEvent& front() const noexcept { return (*this)[0]; }
1517 CommandEvent& operator[](std::size_t index) noexcept {
1518 return overflow_.empty() ? *inline_[index] : overflow_[index];
1519 }
1520 const CommandEvent& operator[](std::size_t index) const noexcept {
1521 return overflow_.empty() ? *inline_[index] : overflow_[index];
1522 }
1523
1524private:
1525 std::array<std::optional<CommandEvent>, 2> inline_{};
1526 std::vector<CommandEvent> overflow_{};
1527 std::size_t size_ = 0;
1528};
1529
1530/// What acceptance needs from the run to judge a command: the point's ordinal and
1531/// coordinate, the price tick the tick spellings resolve against, the quantity
1532/// grid, and the live book. Built by the consumer; a host never constructs one.
1534 int64_t decision_time_ms = 0;
1535 std::optional<double> quantity_grid;
1536 std::optional<OpeningObservation> opening;
1538 std::vector<OpeningObservation> openings;
1539 /// Kernel-resolved acceptance-time units for a Sized{AtAcceptance} request.
1540 /// The execution consumer owns the account facts, so it supplies them here;
1541 /// a producer that leaves it unset accepts the request with a size the
1542 /// matching path then reports as TermsUnresolved. The accepted request
1543 /// carries the value in LiveRequest::sizing_units and still reaches the
1544 /// candidate with a deferred remaining, so the host keeps its one override
1545 /// pass. Appended last so the existing positional aggregate initializers
1546 /// keep their meaning.
1547 std::optional<double> sizing_units;
1548 /// The run's price tick, needed only to resolve a tick-spelled trail
1549 /// offset or trigger anchor. A tick spelling without a usable tick here is
1550 /// rejected rather than silently read as a price distance.
1551 std::optional<double> price_tick;
1552 /// Placement-time sizing measurements the execution consumer owns, supplied
1553 /// only for the intent that asks for them. Appended last, exactly like
1554 /// sizing_units, so the existing positional aggregate initializers keep
1555 /// their meaning.
1556 ///
1557 /// sizing_scope: the bound scope's exposure at acceptance, for a
1558 /// Reduce{ScopeFraction{ScopeBasis::AtAcceptance}}.
1559 /// sizing_price: the frozen signal price, for a Sized whose SizePrice is
1560 /// Signal or SignalOnTick.
1561 /// sizing_admissible: false when the acceptance-resolved quantity of a
1562 /// Sized{SizeTime::AtAcceptance} fails the run's placement admission.
1563 std::optional<double> sizing_scope;
1564 std::optional<double> sizing_price;
1566 /// A Reduce's ExplicitUnits are a FIFO boundary of its scope: the binary64
1567 /// sum, in book order, of the scope's lots through one of them -- the head
1568 /// lot's own size, a prefix, the whole scope. Such a quantity is on the
1569 /// quantity grid whatever the grid predicate answers: these are the book's
1570 /// own quantities, which settlement arithmetic can move off any decimal
1571 /// grid; closing them takes whole lots and splits none (R5 lane K-ULP4).
1572 /// The execution consumer measures it at submit, for a request of
1573 /// ImmediateRemaining capacity; its candidate settles what then remains
1574 /// only on the grid, at a boundary of the scope as it stands or at least at
1575 /// its held total, and refuses anything else (MatchRejectReason::
1576 /// UnrepresentableQuantity). Appended last, like the members above.
1579};
1580
1585 /// Generic current-point delivery. At Open, the consumer sets this only for
1586 /// a market/immediate request born by the pre-open provider. On a continuous
1587 /// OHLC segment, it also admits a request born by an applied callback onto
1588 /// the unconsumed suffix. It is transient and never retained in a request.
1590 /// Set only while resolving a CohortClose candidate. It carries the
1591 /// physical side of the currently live selected roster and is not retained
1592 /// in a request definition.
1593 std::optional<Side> cohort_side;
1594};
1595
1596/// The one policy point of an anchored materialization. The core knows it
1597/// only as a callable over its own values: the leg's live row, the owner's
1598/// fill, the leg's side, the resolved offset (price units) and the kernel
1599/// level after the anchor rounding. It is consulted exactly once per
1600/// materialization, before the ArmedEvent is built, so the ArmedEvent and
1601/// every later reader see the installed level. A returned value is the level
1602/// to install (the kernel's representability check still applies and a
1603/// failure is the existing PreparationError path); nullopt keeps the kernel
1604/// level. An empty callable is exactly the pre-hook behaviour.
1605using AnchoredLevelResolver = std::function<std::optional<double>(
1606 const LiveRequest& leg, const ExecutionAppliedEvent& owner_fill, Side leg_side,
1607 double offset, double kernel_level)>;
1608
1609/// What the consumer hands to prepare_owner_applied for an anchored leg's
1610/// materialization, the way acceptance receives CommandContext::price_tick.
1611/// price_tick is the ladder a rounded anchor snaps to; an anchor whose
1612/// rounding is Raw never reads it. resolve_level is the host's restatement,
1613/// carried as a value so the core stays host-free.
1618
1641
1642/// What the consumer hands to prepare_trigger under an opted-in price grid
1643/// (NativeRunSpec::price_grid == QuantizeFillsAndTriggers), the way acceptance
1644/// receives CommandContext::price_tick: the tick ladder and the rounding the
1645/// matcher tested the activation on. The core then re-validates the reached
1646/// print with the same grid arithmetic (L8b ruling: under that grid the
1647/// tick-quantized print IS the reached price, so a hit the matcher reports is
1648/// never refused), records the quantized print as the activation's reached
1649/// price, and keeps a trail's running best on the ladder. A default-constructed
1650/// value (no ladder) is exactly the raw compare and the raw print every
1651/// activation used before. Host-free: values only.
1652///
1653/// ladder_tick is a different question, appended last so every existing
1654/// aggregate initializer keeps its meaning: the run's DECLARED price tick
1655/// (NativeRunSpec::price_tick), whatever the quantization mode. Nothing is
1656/// tested or rounded on it; it is the ladder a level spelled in ticks names,
1657/// so a trailing stop a whole number of ticks from a best on that ladder is
1658/// the ladder point it names rather than the subtraction's ULP-off neighbour
1659/// (native_matching::ladder_trail_stop, R5 lane E16). Zero leaves the raw
1660/// arithmetic, which is what a run with no declared tick has.
1662 double price_tick = 0.0; // finite and positive when a ladder is in force
1663 bool half_up = true; // nearest tick, ties away from zero; else directional
1664 double ladder_tick = 0.0; // the run's own price tick, quantized or not
1665};
1666
1669 /// Carries the generic pre-open delivery authorization from the matching
1670 /// evaluation through the synchronous execution preparation.
1672 double raw_price = 0.0;
1673 double resolved_price = 0.0;
1681};
1682
1690
1691/// Where an install put its events: `first_index` is an ABSOLUTE journal
1692/// position -- the count of events the run committed before them -- so it
1693/// stays valid when the front of the journal is retired
1694/// (WorkingRequestCore::history_at reads it back).
1696 std::size_t first_index = 0;
1697 std::size_t count = 0;
1698};
1699
1703
1704enum class InstallError : std::uint8_t {
1708};
1709
1710using InstallResult = std::variant<Installed, InstallError>;
1711
1719
1723
1724/// A working-request preparation that could not produce a representable
1725/// instruction — a nonrepresentable quantity, most often. Reported as a settlement
1726/// failure, which fails the run: it is a contract breach, not a refused candidate.
1732
1733template <class T>
1734using Preparation = std::variant<T, NoChange, PreparationError>;
1735
1736template <class R>
1741
1742template <class R>
1743using InstalledCommand = std::variant<CommandInstalled<R>, InstallError>;
1744
1745class PreparedSubmit;
1746class PreparedReplace;
1747class PreparedCancel;
1748class PreparedMutation;
1749class PreparedExecution;
1750
1752 bool birth_ok = false;
1753 bool waiting = false;
1754 bool driver_ok = false;
1755 bool needs_close_bind = false;
1756 bool ready_to_match = false;
1758 bool is_buy = false;
1759 const Trigger* trigger = nullptr;
1760 const TriggerState* trigger_state = nullptr;
1761 const Remaining* remaining = nullptr;
1762 const Allowance* allowance = nullptr;
1763 const Authority* authority = nullptr;
1764};
1765
1768 /// Canonical origin handles, ordered by origin incarnation rather than by
1769 /// host insertion order. A successor is normalized to its predecessor
1770 /// root before entering this table.
1771 std::vector<RequestHandle> origins;
1772};
1773
1774enum class CohortReceiptOperation : std::uint8_t { Add = 0, Remove = 1 };
1787
1788/// One group-effect receipt: the applied event that caused it, the request it
1789/// acted on, the effect, and the ordinal of the event that recorded the
1790/// outcome. A drain that meets the same cause, recipient and effect again finds
1791/// it and applies nothing (WorkingRequestCore::group_effect_receipt).
1798
1799/// Payload row policy for pending batches and committed-row read caches.
1800/// Shared line metadata is additional; retained history/backing admission are
1801/// independent. Zero disables optional residency. One fixed pending record
1802/// per store remains so prepared installation cannot allocate or do I/O.
1804 std::size_t successor_root_rows = 64;
1805 std::size_t receipt_rows = 64;
1806 std::size_t chain_segment_rows = 16;
1807};
1808
1809/// Storage observations only, excluded from continuation state.
1812 std::size_t receipt_capacity_rows = 0;
1814 std::uint64_t successor_root_reads = 0;
1815 std::uint64_t receipt_reads = 0;
1816 std::uint64_t chain_index_reads = 0;
1817 std::uint64_t chain_id_reads = 0;
1818 std::uint64_t successor_root_read_bytes = 0;
1819 std::uint64_t receipt_read_bytes = 0;
1820 std::uint64_t chain_index_read_bytes = 0;
1822 std::size_t receipt_pending_bytes = 0;
1825 std::size_t receipt_control_bytes = 0;
1827};
1828
1830
1832public:
1833 class CohortView;
1834 class JournalView;
1835 class LiveView;
1838 /// Inclusive committed-row read-cache allowances. Pending write batches
1839 /// keep their internal defaults, independently of these optional caches.
1844 detail::ReadThroughCacheStats tail_read_cache_stats(TailWindowKind kind) const noexcept;
1846 WorkingRequestCore& operator=(const WorkingRequestCore&) = delete;
1847 /// Move transfers the complete run. The source becomes empty/unbound;
1848 /// commands throw invalid_argument without mutation until reset rebinds it.
1849 /// Self move-assignment preserves the current run.
1851 WorkingRequestCore& operator=(WorkingRequestCore&& other) noexcept;
1852 void set_successor_roots_directory(const std::string& directory); // After reset, before the first spill; empty uses TMPDIR or /tmp.
1853 void bind_scratch_slab(const std::shared_ptr<::pineforge::detail::ScratchSlab>& slab);
1855
1856 const RunIdentity& identity() const noexcept { return identity_; }
1857 /// The working book, in queue order: ascending LiveRequest::priority,
1858 /// which is incarnation order until a re-price keeps a handle
1859 /// (ReplaceOptions::keep_handle) and takes its request to the back.
1860 /// The book is a read view (LiveView). Up to a fixed bound its rows are
1861 /// resident; a larger book keeps its newest rows outside the heap, in an
1862 /// external ordered table, and the view reads them back in queue order.
1863 /// A row the view hands out stays valid until the next few spilled rows it
1864 /// reads or the next command; a command that targets a row outside the
1865 /// heap brings that row back first.
1866 LiveView live() const noexcept;
1867 /// How many requests are live, resident or not, in O(1); and the request
1868 /// at queue rank `rank` (std::out_of_range when none).
1869 std::size_t live_size() const noexcept;
1870 const LiveRequest& live_at(std::size_t rank) const;
1871 /// Whether some live rows are outside the heap: live_at and live_back
1872 /// then walk the book in queue order instead of indexing it.
1873 bool book_spilled() const noexcept { return spilled_count() != 0; }
1874 /// The command journal's retained window, oldest first. Commits append at
1875 /// the back and retire_history drops a prefix, so the window holds the
1876 /// absolute journal positions [history_base(), history_end()). A core
1877 /// nothing retires -- every core its consumer runs under a retaining
1878 /// policy -- keeps the whole journal and history_base() stays 0.
1879 /// The window is a read view (JournalView): its newest events are resident
1880 /// and its older ones, past a fixed resident bound, live in external
1881 /// pages that the view reads back through a fixed page cache. A reference
1882 /// it hands out stays valid until the next few page loads or the next
1883 /// command; copy an event that must outlive them.
1884 JournalView history() const noexcept;
1885 /// How many events have been retired from the front of the journal.
1886 std::size_t history_base() const noexcept { return history_base_; }
1887 /// The absolute journal length: every event this run has committed,
1888 /// retained or not. EventRange::first_index counts in this space.
1889 std::size_t history_end() const noexcept;
1890 /// The event at an absolute journal position; std::out_of_range when it
1891 /// is not retained.
1892 const CommandEvent& history_at(std::size_t absolute) const;
1893 /// Retire the journal prefix whose ordinals are <= through_ordinal. It
1894 /// stops ahead of the first event a live request still reads -- the head
1895 /// of a deferred group-adjustment chain (collect_pending_chain) -- and
1896 /// drops group-effect keys whose causes are <= retired_through(). The
1897 /// consumer must fold those keys first; queued applied notifications pin
1898 /// their causes. Live rows, logical counters and cohort rosters do not
1899 /// move. Answers the number of journal events retired.
1900 std::size_t retire_history(uint64_t through_ordinal);
1901 /// The ordinal of the newest retired event, 0 while none is.
1902 uint64_t retired_through() const noexcept { return retired_through_; }
1903 /// Logical roster sequence in allocation order, including empty rosters
1904 /// whose members have been removed. Walking allocates nothing and does
1905 /// no I/O; empty rows are synthesized from the dense handle sequence.
1906 CohortView cohorts() const noexcept;
1907 /// The retained cohort-receipt window, in command order. Its absolute
1908 /// positions are [cohort_receipt_base(), cohort_receipt_count()). A
1909 /// standalone core retains all rows until its reader retires them; the
1910 /// Window consumer retires each row after folding it into its digest.
1911 const std::vector<CohortReceipt>& cohort_receipts() const noexcept {
1912 return cohort_receipts_;
1913 }
1914 std::size_t cohort_receipt_base() const noexcept { return cohort_receipts_base_; }
1915 /// Every cohort receipt this run committed, retained or not.
1916 std::size_t cohort_receipt_count() const noexcept {
1917 return cohort_receipts_base_ + cohort_receipts_.size();
1918 }
1919 /// Drop the prefix strictly below the absolute position through, clamped
1920 /// to the window. The reader must have folded these rows first. Does not
1921 /// change rosters, command counters, prepared-command validity or totals.
1922 std::size_t retire_cohort_receipts(std::size_t through) noexcept;
1923 /// The core's two durable counters: the ordinal of the last committed
1924 /// event and the last incarnation a command consumed. A later command must
1925 /// exceed both.
1926 uint64_t last_ordinal() const noexcept { return last_ordinal_; }
1927 uint64_t last_incarnation() const noexcept { return last_incarnation_; }
1928 /// Group-effect keys in cause/commit order, retaining [base, count).
1929 /// Count is the total ever committed; base advances with retire_history.
1930 /// A cause's recipients stay in drain/queue order, even after keep_handle
1931 /// re-pricing. Older-than-newest causes are InvalidCause unless replayed.
1932 /// A retired index asserts in Debug and throws out_of_range otherwise.
1933 /// Only the newest few keys are resident: the rest of the retained window
1934 /// is read back from an external fixed-record log, so a pinned journal
1935 /// (a live deferred chain) no longer grows the heap with its receipts.
1936 std::size_t group_effect_receipt_base() const noexcept;
1937 std::size_t group_effect_receipt_count() const noexcept;
1939 /// The prior adjustment ids a TermsResolvedEvent or QuantityBoundEvent
1940 /// spent, in chain order, as a paged contract: `visit` is called once per
1941 /// id until it answers false. A chain of up to kInlineAdjustmentIds links
1942 /// is in the event's own prior_adjustment_ids exactly as before; a longer
1943 /// one is not copied into the event (a vector of every id would be O(chain
1944 /// length) in the event and in its prepared token) but kept in an external
1945 /// list this call reads back in fixed pages. Answers false when
1946 /// `resolution` names no such event in the retained window.
1947 bool visit_prior_adjustments(const EventId& resolution,
1948 const std::function<bool(const EventId&)>& visit) const;
1949 /// How many prior adjustment ids that event spent (0 when none or unknown).
1950 std::size_t prior_adjustment_count(const EventId& resolution) const;
1951 /// The longest chain whose ids an event still carries inline.
1952 static constexpr std::size_t kInlineAdjustmentIds = 256;
1953 /// The incarnations this run issued -- every accepted request and every
1954 /// replace successor -- as disjoint ascending [first, last] ranges: the
1955 /// half of the chain index that tells a request that ended from one that
1956 /// never existed. Read-only; a consumer issues one dense range.
1957 const std::vector<std::pair<uint64_t, uint64_t>>& issued_incarnations() const noexcept {
1958 return issued_;
1959 }
1960 const LiveRequest* find_live(const RequestHandle& handle) const;
1961 /// find_live for a reader that keeps the row across further reads of the
1962 /// book. A spilled row's copy lives as long as the answer does, whatever
1963 /// the book reads next; a resident row's answer owns nothing and, like
1964 /// find_live's, stays valid until the next command. Null when no live
1965 /// request has the handle.
1966 std::shared_ptr<const LiveRequest> hold_live(const RequestHandle& handle) const;
1967 /// A read of the book's spilled rows that failed where its reader cannot
1968 /// throw (a noexcept fold of the book): the reader hands the failure here
1969 /// and answers true, and from then on every read of the spilled rows and
1970 /// every command that moves one throws it again -- an I/O failure as the
1971 /// typed scratch failure (scratch_io_failed) -- until reset. False, with
1972 /// nothing kept, when the book has never spilled: it then has no such read.
1973 bool carry_book_read_failure(std::exception_ptr failure) const noexcept;
1974 /// Throws the failure carry_book_read_failure kept, if any.
1976 /// How many spilled rows the book has read back to answer a lookup -- by
1977 /// handle (find_live, hold_live), by rank (live_at, live_back) -- and how
1978 /// many table steps those lookups walked, since this core was built or
1979 /// reset. A walk of live() in queue order is not counted, nor is a read of
1980 /// a row at the table entry such a walk found it at (the matcher reusing a
1981 /// row it scanned): neither searches the book. Diagnostics only: nothing
1982 /// folds them and no decision reads them.
1984 std::uint64_t rows = 0;
1985 std::uint64_t steps = 0;
1986 };
1988 const CommandEvent* event_at(const EventId& id) const;
1989
1990 /// Command-boundary roster maintenance. A rejected add/remove records a
1991 /// durable generic receipt but never emits a market event.
1995 bool cohort_contains(CohortHandle cohort, const RequestHandle& opening) const;
1996
1997 /// R1 producer convenience: prepare then install one command. Bound opening
1998 /// enrollment requires CommandContext observations via prepare_submit.
2000 int64_t decision_time_ms,
2001 uint64_t& next_order_incarnation,
2002 uint64_t& next_timeline_ordinal,
2003 std::optional<double> quantity_grid = std::nullopt);
2004
2006 const Request& request,
2007 int64_t decision_time_ms,
2008 uint64_t& next_order_incarnation,
2009 uint64_t& next_timeline_ordinal,
2010 std::optional<double> quantity_grid = std::nullopt,
2011 ReplaceOptions options = {});
2012
2013 CancelResult cancel(const RequestHandle& target, uint64_t& next_timeline_ordinal);
2014
2015 /// Consumer-only typed prepare/install. Prepare may reserve storage and
2016 /// invalidate live/history references; it does not change logical state.
2017 /// Tokens are move-only, bound to this instance/run/epoch, and do not
2018 /// survive another prepare/install, reset, or move.
2020 const CommandContext& context,
2021 uint64_t& next_order_incarnation,
2022 uint64_t& next_timeline_ordinal,
2025 const Request& request,
2026 const CommandContext& context,
2027 uint64_t& next_order_incarnation,
2028 uint64_t& next_timeline_ordinal,
2029 ReplaceOptions options = {});
2030 /// `reason` lets the kernel withdraw its own request under the durable
2031 /// Superseded receipt. Host cancels keep the default User reason.
2032 PreparedCancel prepare_cancel(const RequestHandle& target, uint64_t& next_timeline_ordinal,
2034
2038
2040 const EvaluationContext& context,
2041 const TargetObservation& observation,
2042 uint64_t& next_timeline_ordinal);
2043 /// The same preparation under an explicit open activation grant: the caller
2044 /// vouches, for this handle alone, that the request is to be read as born
2045 /// before this point. The grant satisfies only the strict ordinal half of
2046 /// the birth gate, and only at an Open cursor whose effective time is not
2047 /// before the request's decision-time lower bound. It relaxes no time floor
2048 /// and no trigger, driver, quantity, margin or price check, it leaves the
2049 /// other birth exceptions as they stand, and it gives
2050 /// EvaluationContext::pre_open_birth_eligible no new meaning (that bool stays
2051 /// market/immediate only). Nothing retains it -- no request, plan or prepared
2052 /// token -- so every call that tests the gate takes it afresh; the check and
2053 /// the apply of an execution each do. The form above is this one with the
2054 /// grant false.
2056 const EvaluationContext& context,
2057 const TargetObservation& observation,
2058 uint64_t& next_timeline_ordinal,
2059 bool open_activation_grant);
2060 /// `grid` carries the run's activation grid (ActivationGrid); the default
2061 /// is the raw rule, so every existing caller keeps its meaning.
2063 const TriggerTransition& transition,
2064 DriverEligibilityClass driver_class,
2065 uint64_t& next_timeline_ordinal,
2066 std::optional<Side> cohort_side = std::nullopt,
2067 const ActivationGrid& grid = {});
2068 /// prepare_trigger under the open activation grant of prepare_evaluation: the
2069 /// cursor test every trigger transition makes (Stop, StopLimit and Trail
2070 /// activation among them) takes the grant too. Every parameter is explicit,
2071 /// so no earlier call can resolve to this form.
2073 const TriggerTransition& transition,
2074 DriverEligibilityClass driver_class,
2075 uint64_t& next_timeline_ordinal,
2076 std::optional<Side> cohort_side,
2077 const ActivationGrid& grid,
2078 bool open_activation_grant);
2080
2082 const ExecutionProposal& proposal,
2083 uint64_t& next_timeline_ordinal);
2084 /// prepare_execution under the open activation grant of prepare_evaluation.
2085 /// The token holds no grant: install_execution does not read the birth gate.
2087 const ExecutionProposal& proposal,
2088 uint64_t& next_timeline_ordinal,
2089 bool open_activation_grant);
2091 const CommittedExecutionFacts& facts) noexcept;
2093 const EvaluationContext& context,
2094 uint64_t& next_timeline_ordinal);
2096 const EvaluationContext& context,
2097 MatchRejectReason reason,
2098 uint64_t& next_timeline_ordinal);
2100 const RequestHandle& target,
2101 const EvaluationContext& context,
2102 MatchRejectReason reason,
2103 std::optional<ExecutionTerms> attempted_terms,
2104 uint64_t& next_timeline_ordinal);
2106 const EvaluationContext& context,
2108 uint64_t& next_timeline_ordinal);
2109
2110 /// Append one MarginCallEvent to the immutable history. The target is the
2111 /// kernel-originated request that filled, which is already terminal by the
2112 /// time its margin facts are recorded, so no live row moves.
2114 uint64_t& next_timeline_ordinal);
2115
2116 /// Append one NativeRiskEvent to the immutable history. It names an
2117 /// account-level breach rather than a request, so it moves no live row and
2118 /// carries no definition; the kernel's own response (an opening block, and
2119 /// with FlattenAndBlock a KernelRisk Flatten) is separate from the record.
2121 uint64_t& next_timeline_ordinal);
2122
2123 /// The allowance that prepare_evaluation would install for this point.
2124 static Allowance evaluated_allowance(const LiveRequest& live, uint64_t point) noexcept;
2125 /// Consumer-only no-event form of the ordinary allowance
2126 /// refresh. It preserves prepare_evaluation's eligibility and liveness
2127 /// checks while avoiding a transient mutation envelope per driver point.
2128 void refresh_point_allowances(uint64_t point, const PositionIdentity& position);
2130 const EvaluationContext& context,
2131 const TargetObservation& observation);
2132 /// refresh_allowance under the open activation grant of prepare_evaluation.
2134 const EvaluationContext& context,
2135 const TargetObservation& observation,
2136 bool open_activation_grant);
2137 /// The same refresh, handed the run's timeline: an unbound close that
2138 /// carried a binding the book still has (ReplaceOptions::keep_binding) is
2139 /// bound here too, to the BookClose its CloseBoundEvent would install, and
2140 /// takes that event's ordinal without recording it. The form above leaves
2141 /// every unbound close to the evaluation.
2143 const EvaluationContext& context,
2144 const TargetObservation& observation,
2145 uint64_t& next_timeline_ordinal);
2146 /// The timeline-handed refresh under the open activation grant.
2148 const EvaluationContext& context,
2149 const TargetObservation& observation,
2150 uint64_t& next_timeline_ordinal,
2151 bool open_activation_grant);
2153 const EvaluationContext& context,
2154 const TargetObservation& observation);
2155 /// Pure arithmetic over the cached pending total. Outputs are assigned only
2156 /// after every validation and subtraction succeeds. A total binary64 cannot
2157 /// take off the resolved units is absorbed: the deduction is 0 and the
2158 /// units stand (R5 lane K-ULP5).
2159 static bool effective_host_units(const PendingAdjustments& pending,
2160 double resolved_units,
2161 double* deduction,
2162 double* after,
2163 bool* exhausted) noexcept;
2164
2165 void reserve(std::size_t expected_events);
2166 /// The live siblings an applied member's fill reaches -- its group, another
2167 /// cohort, born before the fill; none for a fill with no effect, such as a
2168 /// Cancel member's nonterminal one -- in queue order (live()): a request
2169 /// re-priced with its handle kept stands behind every sibling accepted or
2170 /// re-priced before it, where a plain replace's successor stands (R5 lane
2171 /// K-OCA-KEEP).
2172 std::vector<RequestHandle> group_recipients(const EventId& applied) const;
2173 std::vector<RequestHandle> waiting_children(const RequestHandle& parent) const;
2174 bool has_waiting_children(const RequestHandle& parent) const;
2175 std::vector<RequestHandle> bound_close_handles() const;
2176 /// The same three lists written over `out`, whose capacity a caller that
2177 /// keeps it reuses (R5 lane L3): the same handles in the same order.
2178 void group_recipients(const EventId& applied, std::vector<RequestHandle>& out) const;
2179 void waiting_children(const RequestHandle& parent, std::vector<RequestHandle>& out) const;
2180 void bound_close_handles(std::vector<RequestHandle>& out) const;
2181 /// Synchronous queue-order reads. Callbacks may read the core or write a
2182 /// separate scratch store; they must not mutate this core.
2186
2188 const RequestHandle& recipient,
2189 uint64_t& next_timeline_ordinal);
2190 /// `arm` carries the materialization facts of an anchored leg (the price
2191 /// tick a rounded anchor snaps to, the host's level restatement); a
2192 /// default-constructed value is exactly the pre-lane behaviour, so every
2193 /// existing caller keeps its meaning.
2195 const EventId& applied,
2196 const RequestHandle& child,
2197 const std::optional<OpeningObservation>& observation,
2198 uint64_t& next_timeline_ordinal,
2199 const ArmContext& arm = {});
2201 const RequestHandle& child,
2202 const TargetObservation& observation,
2203 uint64_t& next_timeline_ordinal);
2205 const RequestHandle& child,
2206 uint64_t& next_timeline_ordinal);
2207
2208 /// The direct forms (R5 lane L3). Each apply_* makes the checks of the
2209 /// prepare_* it is named after, in the same order, and answers what that
2210 /// prepare_* followed at once by its install answers: the same NoChange or
2211 /// PreparationError, the same exception at the same point with nothing
2212 /// changed, and otherwise the same events at the same absolute journal
2213 /// positions, the same live rows, receipts, chain index and counters, and a
2214 /// token prepared before it just as stale. What it does not do is stage:
2215 /// no token, plan or row copy stands between the checks and the write --
2216 /// the live row is updated where it stands and each event is appended
2217 /// once. A caller that installs what it prepares straight away uses these;
2218 /// the prepare/install pairs stay for a caller that holds a token across
2219 /// other work, or that may decide not to install (an anchored arm whose
2220 /// restatement hook failed). An execution's direct form comes in the two
2221 /// halves its caller needs around the settlement it proposes:
2222 /// check_execution makes prepare_execution's checks and its seal (the same
2223 /// NoChange, PreparationError or exception, the same reservation and
2224 /// epoch) and holds nothing; apply_execution, after the settlement, makes
2225 /// them again and then does what install_execution does with the token
2226 /// prepare_execution would have made. A caller changes nothing in the core
2227 /// between the two; if the checks no longer pass, apply_execution answers
2228 /// StalePreparation and writes nothing.
2230 const CommandContext& context,
2231 uint64_t& next_order_incarnation,
2232 uint64_t& next_timeline_ordinal,
2235 const Request& request,
2236 const CommandContext& context,
2237 uint64_t& next_order_incarnation,
2238 uint64_t& next_timeline_ordinal,
2239 ReplaceOptions options = {});
2241 uint64_t& next_timeline_ordinal,
2244 const EvaluationContext& context,
2245 const TargetObservation& observation,
2246 uint64_t& next_timeline_ordinal);
2247 /// apply_evaluation under the open activation grant of prepare_evaluation.
2249 const EvaluationContext& context,
2250 const TargetObservation& observation,
2251 uint64_t& next_timeline_ordinal,
2252 bool open_activation_grant);
2254 const TriggerTransition& transition,
2255 DriverEligibilityClass driver_class,
2256 uint64_t& next_timeline_ordinal,
2257 std::optional<Side> cohort_side = std::nullopt,
2258 const ActivationGrid& grid = {});
2259 /// apply_trigger under the open activation grant of prepare_evaluation; every
2260 /// parameter is explicit, like prepare_trigger's grant form.
2262 const TriggerTransition& transition,
2263 DriverEligibilityClass driver_class,
2264 uint64_t& next_timeline_ordinal,
2265 std::optional<Side> cohort_side,
2266 const ActivationGrid& grid,
2267 bool open_activation_grant);
2269 const EvaluationContext& context,
2270 uint64_t& next_timeline_ordinal);
2272 const EvaluationContext& context,
2273 MatchRejectReason reason,
2274 std::optional<ExecutionTerms> attempted_terms,
2275 uint64_t& next_timeline_ordinal);
2277 const EvaluationContext& context,
2279 uint64_t& next_timeline_ordinal);
2281 uint64_t& next_timeline_ordinal);
2283 uint64_t& next_timeline_ordinal);
2285 const RequestHandle& recipient,
2286 uint64_t& next_timeline_ordinal);
2288 const RequestHandle& child,
2289 const std::optional<OpeningObservation>& observation,
2290 uint64_t& next_timeline_ordinal,
2291 const ArmContext& arm = {});
2293 const RequestHandle& child,
2294 const TargetObservation& observation,
2295 uint64_t& next_timeline_ordinal);
2297 const RequestHandle& child,
2298 uint64_t& next_timeline_ordinal);
2300 const ExecutionProposal& proposal,
2301 uint64_t& next_timeline_ordinal);
2302 /// check_execution and apply_execution under the open activation grant of
2303 /// prepare_evaluation. The two make the same birth test, so a caller hands
2304 /// both the grant its scope holds; an apply given a different one answers
2305 /// StalePreparation like any check that no longer passes.
2307 const ExecutionProposal& proposal,
2308 uint64_t& next_timeline_ordinal,
2309 bool open_activation_grant);
2311 const ExecutionProposal& proposal,
2312 const CommittedExecutionFacts& facts,
2313 uint64_t& next_timeline_ordinal);
2315 const ExecutionProposal& proposal,
2316 const CommittedExecutionFacts& facts,
2317 uint64_t& next_timeline_ordinal,
2318 bool open_activation_grant);
2319
2321 const EvaluationContext& context) const noexcept;
2322 /// eligibility_facts and evaluation_eligible under the open activation grant
2323 /// of prepare_evaluation: the grant can only set birth_ok.
2325 const EvaluationContext& context,
2326 bool open_activation_grant) const noexcept;
2328 const EvaluationContext& context) const noexcept;
2330 const EvaluationContext& context,
2331 bool open_activation_grant) const noexcept;
2332 bool trigger_permits_driver(const Trigger& trigger,
2333 const TriggerState& state,
2334 DriverEligibilityClass driver_class,
2335 bool existing_matching_bit) const noexcept;
2337 std::optional<Side> cohort_side = std::nullopt) const noexcept;
2338 /// Borrow the rosters holding origins, in ascending cohort-handle order.
2339 /// No copies or allocation; use only until the next cohort mutation.
2340 const std::map<std::uint64_t, CohortRoster>& nonempty_cohorts() const noexcept {
2341 return cohorts_;
2342 }
2343
2344private:
2345 enum class TargetKind { Live, NotWorking, InvalidHandle };
2348 const detail::ScratchByteSource&) noexcept;
2350 friend class PreparedSubmit;
2351 friend class PreparedReplace;
2352 friend class PreparedCancel;
2353 friend class PreparedMutation;
2354 friend class PreparedExecution;
2355 // The matcher reuses a row it scanned outside the heap through the place
2356 // its walk found the row at (book_generation, hold_scanned below).
2357 friend class ::pineforge::engine_script_run_v19::NativeExecutionConsumer;
2358
2359 static void require_identity(const RunIdentity& identity);
2360 static void require_grid(std::optional<double> quantity_grid);
2361 static void require_distinct_counters(uint64_t& next_order_incarnation,
2362 uint64_t& next_timeline_ordinal);
2363 std::optional<RequestRejectReason> validate_request(
2364 const Request& request,
2365 const CommandContext& context,
2366 const std::optional<RequestHandle>& replace_target) const;
2367 /// Resolves tick-spelled trail offsets and trigger anchors in place
2368 /// against context.price_tick. Runs after validate_request, on the staged
2369 /// copy that becomes the stored definition.
2370 static std::optional<RequestRejectReason> resolve_tick_spellings(
2371 Request& request, const CommandContext& context);
2372 LiveRequest make_live(DefinitionRef definition,
2373 const CommandContext& context,
2374 EventId accepted) const;
2375 enum class ReceiptLookup : std::uint8_t { Absent = 0, Present = 1, Conflict = 2 };
2376 /// What a chain walk keeps of the chain's ids besides its total: the ids
2377 /// of a chain of up to kInlineAdjustmentIds links, in chain order, or --
2378 /// for a longer one -- where the walk wrote them (an external list of
2379 /// 8-byte ordinals), so no vector of every id is ever built.
2380 struct ChainIds {
2381 std::vector<EventId> inline_ids;
2382 uint64_t count = 0;
2383 bool external = false;
2384 uint64_t offset = 0;
2385 };
2386 /// Walks the live deferred chain `pending` back from its tail, checking
2387 /// every link, then replays it forward in commit order to reproduce the
2388 /// total's exact accumulation. False on any link that does not check.
2389 bool collect_pending_chain(const PendingAdjustments& pending,
2390 const RequestHandle& recipient,
2391 ChainIds* ids,
2392 double* total);
2393 ReceiptLookup receipt_lookup(const EventId& cause, const RequestHandle& recipient,
2394 GroupEffect effect, uint64_t* outcome) const;
2395 bool authenticate_receipt_outcome(const CommandEvent& event, const EventId& cause,
2396 const RequestHandle& recipient, GroupEffect effect) const;
2397 /// The first retained group-effect key whose cause is above `retired`
2398 /// (the end of the keys when none): the base retire_history moves to. Reads
2399 /// the external log, so it may throw; apply_receipt_base cannot.
2400 std::size_t receipts_first_above(uint64_t retired) const;
2401 void apply_receipt_base(std::size_t base) noexcept;
2402 /// The external stores are created on first use, in the directory
2403 /// set_successor_roots_directory named.
2404 void ensure_receipt_store();
2405 CacheCapacity tail_cache_capacity(TailWindowKind kind) const noexcept;
2406 void prepare_codec_frames();
2407 void ensure_chain_segments();
2408 /// Appends one event to the journal (storage reserved by reserve_plan or
2409 /// seal_direct) and advances the absolute length. A bound codec reports I/O.
2410 void append_event(CommandEvent&& event);
2411 /// A first deferred link starts a chain whose head retire_history must
2412 /// keep; a row that held a chain and no longer does makes the cached
2413 /// floor a lower bound until it is recomputed.
2414 void note_chain_birth(const CommandEvent& event) noexcept;
2415 void note_chain_loss(const LiveRequest& row) noexcept;
2416 /// The working book's row erase: every erase goes through here so the
2417 /// chain floor hears of a chain that left with its row.
2418 void erase_live(std::size_t index) noexcept;
2419 /// A best and offset through native_matching::checked_trail_stop: finite,
2420 /// strictly past the best for a positive offset. `ladder_tick` is the run's
2421 /// price tick (ActivationGrid::ladder_tick); zero keeps the raw subtraction.
2422 bool trail_level_ok(double best, double offset, bool is_buy, double* stop,
2423 double ladder_tick = 0.0) const noexcept;
2424 /// Whether this run's core accepted a request under `handle`, live or
2425 /// not: what a cohort command answers UnknownOrigin by.
2426 bool issued(const RequestHandle& handle) const noexcept;
2427 /// The first handle of `origin`'s replace chain (RequestDefinition::root),
2428 /// read from the live row or, for a request that is no longer working,
2429 /// from the chain index; nullopt for a handle this run never issued.
2430 std::optional<RequestHandle> canonical_cohort_origin(const RequestHandle& origin) const;
2431 bool cohort_exists(CohortHandle cohort) const noexcept;
2432 /// Records an accepted request's incarnation and, for a replace
2433 /// successor, its chain root, in the chain index. Storage is reserved by
2434 /// reserve_plan, so this cannot throw inside commit.
2435 void index_committed(const CommandEvent& event) noexcept;
2436 /// The head of every live deferred group-adjustment chain: the lowest
2437 /// ordinal retire_history must keep, or nullopt when no chain is live.
2438 std::optional<uint64_t> pending_chain_floor() const;
2439
2440 uint64_t usable_ordinal(uint64_t next) const;
2441 uint64_t usable_incarnation(uint64_t next) const;
2442 /// The working book's position of the request under `incarnation`, or
2443 /// live_.size(), while repriced_ names a re-priced request: a search by
2444 /// queue order (the priority repriced_ names, or the handle's own number).
2445 std::size_t repriced_position(uint64_t incarnation) const noexcept;
2446 /// refresh_allowance's body, and its bind for an unbound close that
2447 /// carried a binding, under the open activation grant (false for every
2448 /// form that does not name one).
2449 bool refresh_allowance(const RequestHandle& target, const EvaluationContext& context,
2450 const TargetObservation& observation,
2451 uint64_t* next_timeline_ordinal, bool open_activation_grant);
2452 bool refresh_kept_binding(LiveRequest& live, const EvaluationContext& context,
2453 const TargetObservation& observation,
2454 uint64_t& next_timeline_ordinal);
2455 TargetKind classify_repriced(uint64_t incarnation, std::size_t* live_index) const noexcept;
2456 const LiveRequest* find_repriced(uint64_t incarnation) const noexcept;
2457 /// Records that the request under `incarnation` now stands at
2458 /// `priority` (a keep_handle re-price), dropping the entries of requests
2459 /// that have left the book once they outnumber its rows. The caller has
2460 /// reserved one entry.
2461 void note_repriced(uint64_t incarnation, uint64_t priority) noexcept;
2462 /// classify among the resident rows only.
2463 TargetKind classify_resident(const RequestHandle& handle, std::size_t* live_index) const;
2464 /// The index of the row a command targets in live_. A target outside the
2465 /// heap (a spilled row) is brought back into live_ first -- the one place
2466 /// the book changes without a command committing -- so every command
2467 /// reads and writes its row where the resident book keeps it.
2468 TargetKind classify(const RequestHandle& handle, std::size_t* live_index);
2469 bool bump_epoch() noexcept;
2470 void require_epoch_room() const;
2471 void clear_unbound() noexcept;
2472
2473 struct InstanceBinding {
2474 uint64_t generation = 1;
2475 bool expired = false;
2476 };
2477 struct MutationPlan {
2478 std::weak_ptr<InstanceBinding> instance;
2479 uint64_t generation = 0;
2480 uint64_t epoch = 0;
2481 RunIdentity run;
2482 std::size_t history_size = 0;
2483 uint64_t last_ordinal = 0;
2484 bool consumed = false;
2485 InlineCommandEvents events;
2486 std::uint8_t live_change = 0; // 0 none, 1 push, 2 erase, 3 update
2487 std::size_t live_index = 0;
2488 LiveRequest live_row{};
2489 bool consume_incarnation = false;
2490 uint64_t incarnation_used = 0;
2491 // An ordinal the mutation takes with no event: a kept binding's
2492 // (ReplaceOptions::keep_binding). 0: none.
2493 uint64_t skipped_ordinal = 0;
2494 bool add_receipt = false;
2495 EventId receipt_cause{};
2496 RequestHandle receipt_recipient{};
2497 GroupEffect receipt_effect = GroupEffect::Reduce;
2498 uint64_t receipt_outcome = 0;
2499 // A resolution event whose chain is longer than kInlineAdjustmentIds
2500 // keeps its ids in the external list the chain walk wrote: the event's
2501 // ordinal, where the list starts and how many ids it holds.
2502 bool chain_segment = false;
2503 uint64_t chain_event_ordinal = 0;
2504 uint64_t chain_offset = 0;
2505 uint64_t chain_count = 0;
2506 };
2507 std::optional<InstallError> validate_plan(const MutationPlan& plan) const noexcept;
2508 // prepare_replace / apply_replace past their checks, for a replace that
2509 // keeps the handle or carries a binding (ReplaceOptions::keep_handle /
2510 // keep_binding), and the successor row the two share.
2511 PreparedReplace prepare_replace_kept(std::size_t live_index, const RequestHandle& staged_target,
2512 Request&& staged, uint64_t ordinal, uint64_t incarnation,
2513 const CommandContext& context, ReplaceOptions options,
2514 MutationPlan&& plan);
2515 // apply_replace's two instantiations: <false> is a plain replace,
2516 // <true> (through apply_replace_kept, out of line) one with the options.
2517 template <bool Kept>
2518 CommandInstalled<ReplaceResult> apply_replace_as(const RequestHandle& target,
2519 const Request& request,
2520 const CommandContext& context,
2521 uint64_t& next_order_incarnation,
2522 uint64_t& next_timeline_ordinal,
2523 ReplaceOptions options);
2524 CommandInstalled<ReplaceResult> apply_replace_kept(const RequestHandle& target,
2525 const Request& request,
2526 const CommandContext& context,
2527 uint64_t& next_order_incarnation,
2528 uint64_t& next_timeline_ordinal,
2529 ReplaceOptions options);
2530 LiveRequest kept_successor(std::size_t live_index, const RequestHandle& staged_target,
2531 Request&& staged, uint64_t ordinal, uint64_t incarnation,
2532 const CommandContext& context, ReplaceOptions options) const;
2533 InstallResult commit(MutationPlan& plan) noexcept;
2534 MutationPlan begin_plan() const;
2535 void reserve_plan(const MutationPlan& plan);
2536 void bind_plan(MutationPlan& plan) const;
2537 void seal_plan(MutationPlan& plan);
2538 PreparedMutation finish_mutation(MutationPlan plan);
2539
2540 // The direct forms' halves of a plan (R5 lane L3). begin_direct makes
2541 // begin_plan's checks; seal_direct reserves what seal_plan would reserve
2542 // for `events` events and the live change and takes the seal's epoch;
2543 // append_direct appends one event exactly as commit does (the journal
2544 // append, the chain index for a command that consumes an incarnation,
2545 // the last ordinal); finish_direct takes commit's epoch and answers the
2546 // range. Nothing after seal_direct can throw. seal_direct also makes the
2547 // journal's room -- it may write the oldest resident events to an
2548 // external page first -- so a pointer into the journal taken before it is
2549 // read before it, as for prepare_*. `chain_segment` reserves the external
2550 // list index row of a resolution whose chain was written externally.
2551 void begin_direct() const;
2552 void seal_direct(std::size_t events, bool push_live, bool add_receipt,
2553 bool consume_incarnation, bool chain_segment = false);
2554 /// Records the external id list of a resolution event just appended by
2555 /// append_direct (room made by seal_direct). Cannot throw.
2556 void note_chain_segment(uint64_t event_ordinal, uint64_t offset, uint64_t count) noexcept;
2557 template <class Event>
2558 void append_direct(Event&& event, bool consume_incarnation);
2559 EventRange finish_direct(std::size_t first) noexcept;
2560 template <class Event>
2561 Installed end_direct(std::size_t live_index, Event&& event);
2562 // What prepare_execution computes for its events and its retained row,
2563 // as values: check_execution and apply_execution make the same checks
2564 // through it (execution_values), so what apply_execution writes is what
2565 // the token would have held.
2566 struct ExecutionValues {
2567 std::size_t live_index = 0;
2568 uint64_t ordinal = 0;
2569 double filled = 0.0;
2570 bool flatten = false;
2571 bool units_exhausted = false;
2572 RemainingProjection remaining_after = RemainingProjectionUnits{};
2573 Allowance allowance_after = AllowanceUnset{};
2574 ExecutionScope scope = execution::Book{};
2575 };
2576 Preparation<ExecutionValues> execution_values(const RequestHandle& target,
2577 const ExecutionProposal& proposal,
2578 uint64_t& next_timeline_ordinal,
2579 bool open_activation_grant);
2580
2581 // The journal, the group-effect keys and the long
2582 // chains' id lists keep a fixed resident part and write the rest to
2583 // run-owned scratch files, created on the first spill. They are defined in
2584 // native_order.cpp and held by shared_ptr so this header stays complete.
2585 struct JournalStore;
2586 struct ReceiptStore;
2587 struct ChainSegments;
2588 // E02: the working book keeps at most a fixed number of rows in live_,
2589 // sorted by priority; its newest rows beyond that wait in BookSpill (an
2590 // external ordered table of serialized rows), spilled_count() of them.
2591 // The queue is the merge of the two in priority order. A row is in
2592 // exactly one of them; a command that targets a spilled row brings it
2593 // back into live_ (classify), and a row that would not fit pushes the
2594 // newest resident rows out first (make_book_room). repriced_ names only
2595 // resident rows.
2596 // The stores' state lives in the stores (the journal's length, the keys'
2597 // base and count, the spilled rows' count) and the directory they spill
2598 // into is the successor roots' (scratch_directory), so that the core
2599 // itself keeps the size it had before the stores existed.
2600 struct BookSpill;
2601 std::size_t spilled_count() const noexcept;
2602 const std::string& scratch_directory() const noexcept;
2603 // A position in the merged queue: the next resident row and the next
2604 // spilled one (a table offset, 0 when none) with its priority.
2605 struct BookCursor {
2606 std::size_t r = 0;
2607 uint64_t at = 0;
2608 uint64_t spilled_priority = 0;
2609 };
2610 void make_book_room();
2611 bool promote_book_row(uint64_t incarnation, std::size_t* index);
2612 const LiveRequest* spilled_row(uint64_t incarnation) const;
2613 std::shared_ptr<LiveRequest> spilled_held(uint64_t incarnation) const;
2614 const LiveRequest* find_resident(const RequestHandle& handle) const noexcept;
2615 // How many times the spilled table has been rebuilt (BookSpill::compact):
2616 // a table offset taken under one generation names nothing under the next.
2617 uint64_t book_generation() const noexcept;
2618 // hold_live for a row a walk of live() stood on outside the heap, at table
2619 // offset `at` (LiveView::const_iterator::spilled_at) under `generation`:
2620 // the row is read from that entry, with no search of the table, while the
2621 // entry still holds the request spilled. Otherwise -- the table rebuilt
2622 // since, the row back in the heap or ended -- it answers hold_live(handle).
2623 std::shared_ptr<const LiveRequest> hold_scanned(const RequestHandle& handle, uint64_t at,
2624 uint64_t generation) const;
2625 void forget_repriced(uint64_t incarnation) noexcept;
2626 BookCursor book_begin() const;
2627 bool book_done(const BookCursor& cursor) const noexcept;
2628 bool book_next_spilled(const BookCursor& cursor) const noexcept;
2629 void book_step(BookCursor& cursor) const;
2630 const LiveRequest& live_back() const;
2631 /// Visits every live row in queue order until `visit` answers false. A
2632 /// spilled row is a copy that lives only for its visit.
2633 template <class Visit>
2634 void visit_book(Visit&& visit) const;
2635
2636 RunIdentity identity_;
2637 std::shared_ptr<InstanceBinding> instance_;
2638 std::vector<LiveRequest> live_;
2639 std::shared_ptr<BookSpill> book_spill_;
2640 uint64_t last_ordinal_ = 0;
2641 uint64_t last_incarnation_ = 0;
2642 uint64_t epoch_ = 0;
2643 // The journal window: how many events were retired from its front and the
2644 // ordinal of the newest retired event. The events themselves -- resident
2645 // chunks plus external pages, the ordinal lookup over both, and the
2646 // absolute length (the position the next event takes) -- are the store's.
2647 std::shared_ptr<JournalStore> journal_;
2648 std::size_t history_base_ = 0;
2649 uint64_t retired_through_ = 0;
2650 // The head of the oldest live deferred chain (kNoChainFloor: none), a
2651 // lower bound unless chain_floor_exact_ (see note_chain_birth /
2652 // note_chain_loss).
2653 static constexpr uint64_t kNoChainFloor = std::numeric_limits<uint64_t>::max();
2654 mutable uint64_t chain_floor_ = kNoChainFloor;
2655 mutable bool chain_floor_exact_ = true;
2656 std::shared_ptr<ChainSegments> chain_segments_;
2657 // Issued ranges are dense for a consumer. Successor roots spill in order;
2658 // the consumer's event fold hashes their logical rows, once each.
2659 struct SuccessorRootIndex;
2660 void reserve_successor_root();
2661 void append_successor_root(uint64_t incarnation, uint64_t root) noexcept;
2662 std::optional<uint64_t> successor_root(uint64_t incarnation) const;
2663 std::vector<std::pair<uint64_t, uint64_t>> issued_;
2664 std::shared_ptr<SuccessorRootIndex> successor_roots_;
2665 // The requests a keep_handle re-price moved in the queue, ascending by
2666 // incarnation: (incarnation, priority). The book is held by priority, so
2667 // a handle lookup reads its priority here; a handle absent here is at its
2668 // own incarnation. Derived from the book (each row's definition carries
2669 // its priority), so nothing folds it; an entry outlives its request
2670 // until the next re-price prunes it, and a lookup through it finds no
2671 // row because no other request takes that priority.
2672 std::vector<std::pair<uint64_t, uint64_t>> repriced_;
2673 // Readback-only base; total/digest stay in the consumer. Keys are in
2674 // cause/drain order, retained at [group_effect_receipt_base(),
2675 // group_effect_receipt_count()) and held by the receipt store (a resident
2676 // tail over an external log), which also keeps the base and the count.
2677 std::shared_ptr<ReceiptStore> receipt_store_;
2678 std::uint64_t next_cohort_handle_ = 1;
2679 // Only rosters with members occupy nodes; erasing an empty roster frees
2680 // its member capacity as well. next_cohort_handle_ records the complete
2681 // dense sequence of allocated handles, including every empty roster.
2682 std::map<std::uint64_t, CohortRoster> cohorts_;
2683 std::vector<CohortReceipt> cohort_receipts_;
2684 // Storage cursor only: the consumer hashes the lifetime count and every
2685 // receipt, independently of how many of their rows have been retired.
2686 std::size_t cohort_receipts_base_ = 0;
2687};
2688
2689// A non-owning read view. Iteration preserves CohortRoster references for
2690// existing typed predicates and iterator arrow reads. An empty row belongs
2691// to its input iterator and is valid until that iterator advances or dies.
2692// Indexed reads return a value, so a saved row from a temporary view remains
2693// valid. Callers that need no copies use the allocation-free iterator walk.
2694class WorkingRequestCore::CohortView {
2695public:
2697 public:
2698 using iterator_category = std::input_iterator_tag;
2700 using difference_type = std::ptrdiff_t;
2701 using pointer = const CohortRoster*;
2702 using reference = const CohortRoster&;
2703
2704 const_iterator() noexcept = default;
2705 reference operator*() const noexcept;
2706 pointer operator->() const noexcept { return &**this; }
2707 const_iterator& operator++() noexcept { ++index_; return *this; }
2709 auto previous = *this;
2710 ++*this;
2711 return previous;
2712 }
2713 friend bool operator==(const const_iterator& left, const const_iterator& right) noexcept {
2714 return left.core_ == right.core_ && left.index_ == right.index_;
2715 }
2716 friend bool operator!=(const const_iterator& left, const const_iterator& right) noexcept {
2717 return !(left == right);
2718 }
2719
2720 private:
2721 friend class CohortView;
2722 const_iterator(const WorkingRequestCore* core, std::size_t index) noexcept
2723 : core_(core), index_(index) {}
2724 const WorkingRequestCore* core_ = nullptr;
2725 std::size_t index_ = 0;
2726 mutable CohortRoster empty_{};
2727 };
2728
2729 std::size_t size() const noexcept {
2730 return static_cast<std::size_t>(core_->next_cohort_handle_ - 1);
2731 }
2732 bool empty() const noexcept { return size() == 0; }
2733 const_iterator begin() const noexcept { return const_iterator(core_, 0); }
2734 const_iterator end() const noexcept { return const_iterator(core_, size()); }
2735 CohortRoster operator[](std::size_t index) const { return *const_iterator(core_, index); }
2736
2737private:
2739 explicit CohortView(const WorkingRequestCore* core) noexcept : core_(core) {}
2740 const WorkingRequestCore* core_;
2741};
2742
2743// A non-owning read view of the command journal's retained window
2744// (WorkingRequestCore::history), with the read interface the window had as a
2745// std::vector<CommandEvent>: size, empty, front, back, indexing relative to
2746// history_base(), random-access iteration, and a conversion to a vector for
2747// a reader that wants an owning copy. The newest events are resident; older
2748// ones sit in external pages that the view reads back through a fixed page
2749// cache, so a reference it returns stays valid until the next few page loads
2750// or the next command, and a read of an old event may do I/O (and so throw).
2751// capacity() and max_size() answer one huge bound: nothing is reserved for an
2752// external window. Positions are absolute inside the iterator, so an
2753// iterator survives a retirement that leaves its event in the window.
2755public:
2757 public:
2758 using iterator_category = std::random_access_iterator_tag;
2760 using difference_type = std::ptrdiff_t;
2761 using pointer = const CommandEvent*;
2762 using reference = const CommandEvent&;
2763
2764 const_iterator() noexcept = default;
2765 reference operator*() const { return core_->history_at(position_); }
2766 pointer operator->() const { return &core_->history_at(position_); }
2767 reference operator[](difference_type offset) const { return *(*this + offset); }
2768 const_iterator& operator++() noexcept { ++position_; return *this; }
2770 const_iterator previous = *this;
2771 ++position_;
2772 return previous;
2773 }
2774 const_iterator& operator--() noexcept { --position_; return *this; }
2776 const_iterator previous = *this;
2777 --position_;
2778 return previous;
2779 }
2781 position_ = static_cast<std::size_t>(static_cast<difference_type>(position_) + offset);
2782 return *this;
2783 }
2784 const_iterator& operator-=(difference_type offset) noexcept { return *this += -offset; }
2786 iterator += offset;
2787 return iterator;
2788 }
2790 iterator += offset;
2791 return iterator;
2792 }
2794 iterator -= offset;
2795 return iterator;
2796 }
2798 const const_iterator& right) noexcept {
2799 return static_cast<difference_type>(left.position_)
2800 - static_cast<difference_type>(right.position_);
2801 }
2802 friend bool operator==(const const_iterator& left, const const_iterator& right) noexcept {
2803 return left.position_ == right.position_;
2804 }
2805 friend bool operator!=(const const_iterator& left, const const_iterator& right) noexcept {
2806 return left.position_ != right.position_;
2807 }
2808 friend bool operator<(const const_iterator& left, const const_iterator& right) noexcept {
2809 return left.position_ < right.position_;
2810 }
2811 friend bool operator>(const const_iterator& left, const const_iterator& right) noexcept {
2812 return left.position_ > right.position_;
2813 }
2814 friend bool operator<=(const const_iterator& left, const const_iterator& right) noexcept {
2815 return left.position_ <= right.position_;
2816 }
2817 friend bool operator>=(const const_iterator& left, const const_iterator& right) noexcept {
2818 return left.position_ >= right.position_;
2819 }
2820
2821 private:
2822 friend class JournalView;
2823 const_iterator(const WorkingRequestCore* core, std::size_t position) noexcept
2824 : core_(core), position_(position) {}
2825 const WorkingRequestCore* core_ = nullptr;
2826 std::size_t position_ = 0;
2827 };
2829 using size_type = std::size_t;
2830 using reference = const CommandEvent&;
2833
2834 std::size_t size() const noexcept { return core_->history_end() - core_->history_base(); }
2835 bool empty() const noexcept { return size() == 0; }
2836 std::size_t max_size() const noexcept {
2837 return static_cast<std::size_t>(std::numeric_limits<std::ptrdiff_t>::max()) / 4;
2838 }
2839 std::size_t capacity() const noexcept { return max_size(); }
2840 const CommandEvent& operator[](std::size_t index) const {
2841 return core_->history_at(core_->history_base() + index);
2842 }
2843 const CommandEvent& at(std::size_t index) const {
2844 if (index >= size()) throw std::out_of_range("native journal index is not retained");
2845 return (*this)[index];
2846 }
2847 const CommandEvent& front() const { return (*this)[0]; }
2848 const CommandEvent& back() const { return (*this)[size() - 1]; }
2849 const_iterator begin() const noexcept { return const_iterator(core_, core_->history_base()); }
2850 const_iterator end() const noexcept { return const_iterator(core_, core_->history_end()); }
2851 const_iterator cbegin() const noexcept { return begin(); }
2852 const_iterator cend() const noexcept { return end(); }
2853 operator std::vector<CommandEvent>() const { return std::vector<CommandEvent>(begin(), end()); }
2854
2855private:
2857 explicit JournalView(const WorkingRequestCore* core) noexcept : core_(core) {}
2858 const WorkingRequestCore* core_;
2859};
2860
2861// A non-owning read view of the working book in queue order
2862// (WorkingRequestCore::live), with the read interface the book had as a
2863// std::vector<LiveRequest>: size, empty, front, back, indexing by queue rank,
2864// forward iteration, and a conversion to a vector for a reader that wants an
2865// owning copy. The resident rows are read in place; the rows outside the heap
2866// (a book past its resident bound) are read back one at a time, in queue
2867// order, so an iteration holds at most one of them. A reference to a spilled
2868// row stays valid until the next few spilled rows are read or the next
2869// command. Iterating costs one external read per spilled row; indexing by rank
2870// walks to it.
2872public:
2874 public:
2875 using iterator_category = std::forward_iterator_tag;
2877 using difference_type = std::ptrdiff_t;
2878 using pointer = const LiveRequest*;
2879 using reference = const LiveRequest&;
2880
2881 const_iterator() noexcept = default;
2882 reference operator*() const;
2883 pointer operator->() const { return &**this; }
2884 /// The row the iterator stands on, held as hold_live holds it: a
2885 /// spilled row stays valid as long as the answer does, however far
2886 /// the iterator or any other reader of the book moves on.
2887 std::shared_ptr<const LiveRequest> hold() const;
2890 const_iterator previous = *this;
2891 ++*this;
2892 return previous;
2893 }
2894 friend bool operator==(const const_iterator& left, const const_iterator& right) noexcept {
2895 return left.core_ == right.core_ && left.cursor_.r == right.cursor_.r
2896 && left.cursor_.at == right.cursor_.at;
2897 }
2898 friend bool operator!=(const const_iterator& left, const const_iterator& right) noexcept {
2899 return !(left == right);
2900 }
2901
2902 private:
2903 friend class LiveView;
2904 friend class ::pineforge::engine_script_run_v19::NativeExecutionConsumer;
2905 const_iterator(const WorkingRequestCore* core, BookCursor cursor) noexcept
2906 : core_(core), cursor_(cursor) {}
2907 // The spilled table's offset of the row the iterator stands on, 0
2908 // when that row is resident (WorkingRequestCore::hold_scanned).
2909 uint64_t spilled_at() const noexcept { return held_ ? cursor_.at : 0; }
2910 // Loads the spilled row the cursor stands on, if it does.
2911 void settle();
2912 const WorkingRequestCore* core_ = nullptr;
2913 BookCursor cursor_{};
2914 std::shared_ptr<LiveRequest> held_;
2915 };
2917 using size_type = std::size_t;
2918 using reference = const LiveRequest&;
2921
2922 std::size_t size() const noexcept { return core_->live_size(); }
2923 bool empty() const noexcept { return size() == 0; }
2924 const LiveRequest& operator[](std::size_t rank) const { return core_->live_at(rank); }
2925 const LiveRequest& at(std::size_t rank) const { return core_->live_at(rank); }
2926 const LiveRequest& front() const { return core_->live_at(0); }
2927 const LiveRequest& back() const { return core_->live_back(); }
2930 const_iterator cbegin() const { return begin(); }
2931 const_iterator cend() const { return end(); }
2932 operator std::vector<LiveRequest>() const;
2933
2934private:
2936 explicit LiveView(const WorkingRequestCore* core) noexcept : core_(core) {}
2937 const WorkingRequestCore* core_;
2938};
2939
2941public:
2942 PreparedSubmit() noexcept;
2944 PreparedSubmit& operator=(PreparedSubmit&&) noexcept;
2947 PreparedSubmit& operator=(const PreparedSubmit&) = delete;
2948 explicit operator bool() const noexcept;
2949 const SubmitResult& predicted() const;
2951
2952private:
2954 struct Impl;
2955 std::unique_ptr<Impl> impl_;
2956 explicit PreparedSubmit(std::unique_ptr<Impl> impl) noexcept;
2957};
2958
2960public:
2963 PreparedReplace& operator=(PreparedReplace&&) noexcept;
2966 PreparedReplace& operator=(const PreparedReplace&) = delete;
2967 explicit operator bool() const noexcept;
2968 const ReplaceResult& predicted() const;
2970
2971private:
2973 struct Impl;
2974 std::unique_ptr<Impl> impl_;
2975 explicit PreparedReplace(std::unique_ptr<Impl> impl) noexcept;
2976};
2977
2979public:
2980 PreparedCancel() noexcept;
2982 PreparedCancel& operator=(PreparedCancel&&) noexcept;
2985 PreparedCancel& operator=(const PreparedCancel&) = delete;
2986 explicit operator bool() const noexcept;
2987 const CancelResult& predicted() const;
2989
2990private:
2992 struct Impl;
2993 std::unique_ptr<Impl> impl_;
2994 explicit PreparedCancel(std::unique_ptr<Impl> impl) noexcept;
2995};
2996
2998public:
3001 PreparedMutation& operator=(PreparedMutation&&) noexcept;
3004 PreparedMutation& operator=(const PreparedMutation&) = delete;
3005 explicit operator bool() const noexcept;
3006
3007private:
3009 struct Impl;
3010 std::unique_ptr<Impl> impl_;
3011 explicit PreparedMutation(std::unique_ptr<Impl> impl) noexcept;
3012};
3013
3015public:
3018 PreparedExecution& operator=(PreparedExecution&&) noexcept;
3021 PreparedExecution& operator=(const PreparedExecution&) = delete;
3022 explicit operator bool() const noexcept;
3023
3024private:
3026 struct Impl;
3027 std::unique_ptr<Impl> impl_;
3028 explicit PreparedExecution(std::unique_ptr<Impl> impl) noexcept;
3029};
3030
3031static_assert(std::is_nothrow_move_constructible_v<Request>);
3032static_assert(std::is_nothrow_move_assignable_v<Request>);
3033static_assert(std::is_nothrow_move_constructible_v<LiveRequest>);
3034static_assert(std::is_nothrow_move_assignable_v<LiveRequest>);
3035static_assert(std::is_nothrow_move_constructible_v<CommandEvent>);
3036static_assert(std::is_nothrow_move_assignable_v<CommandEvent>);
3037static_assert(std::is_nothrow_move_constructible_v<SubmitResult>);
3038static_assert(std::is_nothrow_move_constructible_v<ReplaceResult>);
3039static_assert(std::is_nothrow_move_constructible_v<CancelResult>);
3040static_assert(std::is_nothrow_move_constructible_v<NoEffectEvent>);
3041static_assert(std::is_nothrow_move_constructible_v<MatchRejectedEvent>);
3042static_assert(std::is_nothrow_move_constructible_v<ExecutionAppliedEvent>);
3043static_assert(std::is_nothrow_move_constructible_v<MatchCursor>);
3044static_assert(std::variant_size_v<OrderIntent> == 6);
3045static_assert(std::variant_size_v<ReductionSize> == 3);
3046static_assert(std::variant_size_v<SizeBasis> == 2);
3047static_assert(std::variant_size_v<Remaining> == 5);
3048static_assert(std::variant_size_v<RemainingProjection> == 5);
3049static_assert(std::variant_size_v<Allowance> == 4);
3050static_assert(std::variant_size_v<CommandEvent> == 19);
3051static_assert(std::variant_size_v<ExecutionPlan> == 4);
3052static_assert(std::variant_size_v<ExecutionScope> == 3);
3053static_assert(std::variant_size_v<TriggerState> == 9);
3054static_assert(std::variant_size_v<TriggerAnchor> == 2);
3055
3056} // inline namespace native_order_v8
3057} // namespace pineforge::native_order
3058
3059namespace std {
3060template <>
3061struct hash<pineforge::native_order::CohortHandle> {
3062 std::size_t operator()(pineforge::native_order::CohortHandle value) const noexcept {
3063 return static_cast<std::size_t>(value.value ^ (value.value >> 32));
3064 }
3065};
3066} // namespace std
All reads borrow store/version; no owning row or text reconstruction.
ScratchHandle version() const noexcept
PayloadReadback() noexcept=default
ScratchStatus find(std::string_view name, PayloadField &out) const noexcept
Almost every prepared command yields one history event.
CommandEvent & operator[](std::size_t index) noexcept
const CommandEvent & operator[](std::size_t index) const noexcept
InlineCommandEvents & operator=(InlineCommandEvents &&other) noexcept
InlineCommandEvents & operator=(const InlineCommandEvents &other)
detail::ScratchStatus read(std::uint64_t, detail::ScratchBuffer) const noexcept override
detail::ScratchStatus opening_at(std::uint64_t index, RequestHandle &out) const noexcept override
OpeningInputCursor(std::uint64_t count, void *context, Reader reader) noexcept
detail::ScratchStatus(*)(void *, std::uint64_t, RequestHandle &) Reader
Borrowed through the enclosing command/evaluation and any prepare/install pair that copies its Target...
virtual detail::ScratchStatus observation_at(std::uint64_t index, OpeningObservation &out) const noexcept=0
Immutable membership, independent of a source scratch scope.
virtual detail::ScratchStatus opening_at(std::uint64_t index, RequestHandle &out) const noexcept=0
virtual std::uint64_t count() const noexcept=0
RequestReadback(detail::PayloadReadback view) noexcept
detail::ScratchStatus comment(detail::PayloadField &out) const noexcept
detail::ScratchStatus label(detail::PayloadField &out) const noexcept
friend bool operator==(const const_iterator &left, const const_iterator &right) noexcept
friend bool operator!=(const const_iterator &left, const const_iterator &right) noexcept
friend bool operator==(const const_iterator &left, const const_iterator &right) noexcept
friend bool operator>(const const_iterator &left, const const_iterator &right) noexcept
friend const_iterator operator+(const_iterator iterator, difference_type offset) noexcept
friend bool operator>=(const const_iterator &left, const const_iterator &right) noexcept
friend const_iterator operator+(difference_type offset, const_iterator iterator) noexcept
friend bool operator<(const const_iterator &left, const const_iterator &right) noexcept
friend bool operator!=(const const_iterator &left, const const_iterator &right) noexcept
friend difference_type operator-(const const_iterator &left, const const_iterator &right) noexcept
friend bool operator<=(const const_iterator &left, const const_iterator &right) noexcept
friend const_iterator operator-(const_iterator iterator, difference_type offset) noexcept
friend bool operator==(const const_iterator &left, const const_iterator &right) noexcept
std::shared_ptr< const LiveRequest > hold() const
The row the iterator stands on, held as hold_live holds it: a spilled row stays valid as long as the ...
friend bool operator!=(const const_iterator &left, const const_iterator &right) noexcept
const CommandEvent & history_at(std::size_t absolute) const
The event at an absolute journal position; std::out_of_range when it is not retained.
Preparation< Installed > apply_margin_call(const MarginCallEvent &event, uint64_t &next_timeline_ordinal)
bool refresh_allowance(const RequestHandle &target, const EvaluationContext &context, const TargetObservation &observation, uint64_t &next_timeline_ordinal, bool open_activation_grant)
The timeline-handed refresh under the open activation grant.
const LiveRequest * find_live(const RequestHandle &handle) const
void waiting_children(const RequestHandle &parent, std::vector< RequestHandle > &out) const
Preparation< PreparedMutation > prepare_evaluation(const RequestHandle &target, const EvaluationContext &context, const TargetObservation &observation, uint64_t &next_timeline_ordinal)
LiveView live() const noexcept
The working book, in queue order: ascending LiveRequest::priority, which is incarnation order until a...
Preparation< PreparedExecution > prepare_execution(const RequestHandle &target, const ExecutionProposal &proposal, uint64_t &next_timeline_ordinal)
const CommandEvent * event_at(const EventId &id) const
void visit_group_recipients(const EventId &, void *, HandleVisitor) const
Synchronous queue-order reads.
void set_successor_roots_directory(const std::string &directory)
uint64_t retired_through() const noexcept
The ordinal of the newest retired event, 0 while none is.
void visit_bound_close_handles(void *, HandleVisitor) const
std::size_t prior_adjustment_count(const EventId &resolution) const
How many prior adjustment ids that event spent (0 when none or unknown).
Preparation< std::monostate > check_execution(const RequestHandle &target, const ExecutionProposal &proposal, uint64_t &next_timeline_ordinal)
CommandInstalled< ReplaceResult > apply_replace(const RequestHandle &target, const Request &request, const CommandContext &context, uint64_t &next_order_incarnation, uint64_t &next_timeline_ordinal, ReplaceOptions options={})
Preparation< Installed > apply_trigger(const RequestHandle &target, const TriggerTransition &transition, DriverEligibilityClass driver_class, uint64_t &next_timeline_ordinal, std::optional< Side > cohort_side=std::nullopt, const ActivationGrid &grid={})
bool refresh_allowance(const RequestHandle &target, const EvaluationContext &context, const TargetObservation &observation)
PreparedReplace prepare_replace(const RequestHandle &target, const Request &request, const CommandContext &context, uint64_t &next_order_incarnation, uint64_t &next_timeline_ordinal, ReplaceOptions options={})
Preparation< Installed > apply_parent_terminal(const EventId &terminal_or_replaced, const RequestHandle &child, uint64_t &next_timeline_ordinal)
InstallResult install_execution(PreparedExecution &&prepared, const CommittedExecutionFacts &facts) noexcept
std::size_t live_size() const noexcept
How many requests are live, resident or not, in O(1); and the request at queue rank rank (std::out_of...
CommandInstalled< CancelResult > apply_cancel(const RequestHandle &target, uint64_t &next_timeline_ordinal, CancelReason reason=CancelReason::User)
CohortView cohorts() const noexcept
Logical roster sequence in allocation order, including empty rosters whose members have been removed.
std::size_t retire_cohort_receipts(std::size_t through) noexcept
Drop the prefix strictly below the absolute position through, clamped to the window.
bool evaluation_eligible(const LiveRequest &live, const EvaluationContext &context) const noexcept
Preparation< Installed > apply_risk_event(const NativeRiskEvent &event, uint64_t &next_timeline_ordinal)
friend detail::ScratchStatus append_journal_record(WorkingRequestCore &, const detail::ScratchByteSource &) noexcept
Preparation< PreparedMutation > prepare_risk_event(const NativeRiskEvent &event, uint64_t &next_timeline_ordinal)
Append one NativeRiskEvent to the immutable history.
Preparation< PreparedMutation > prepare_terms(const RequestHandle &target, const EvaluationContext &context, const TermsResolvedInput &input, uint64_t &next_timeline_ordinal)
bool cohort_contains(CohortHandle cohort, const RequestHandle &opening) const
bool book_spilled() const noexcept
Whether some live rows are outside the heap: live_at and live_back then walk the book in queue order ...
static constexpr std::size_t kInlineAdjustmentIds
The longest chain whose ids an event still carries inline.
bool working_is_buy(const LiveRequest &live, std::optional< Side > cohort_side=std::nullopt) const noexcept
std::size_t group_effect_receipt_base() const noexcept
Group-effect keys in cause/commit order, retaining [base, count).
EligibilityFacts eligibility_facts(const LiveRequest &live, const EvaluationContext &context, bool open_activation_grant) const noexcept
eligibility_facts and evaluation_eligible under the open activation grant of prepare_evaluation: the ...
bool refresh_allowance(const RequestHandle &target, const EvaluationContext &context, const TargetObservation &observation, uint64_t &next_timeline_ordinal)
The same refresh, handed the run's timeline: an unbound close that carried a binding the book still h...
const std::vector< std::pair< uint64_t, uint64_t > > & issued_incarnations() const noexcept
The incarnations this run issued – every accepted request and every replace successor – as disjoint a...
Preparation< PreparedMutation > prepare_evaluation(const RequestHandle &target, const EvaluationContext &context, const TargetObservation &observation, uint64_t &next_timeline_ordinal, bool open_activation_grant)
The same preparation under an explicit open activation grant: the caller vouches, for this handle alo...
std::vector< RequestHandle > waiting_children(const RequestHandle &parent) const
InstalledCommand< SubmitResult > install_submit(PreparedSubmit &&prepared) noexcept
InstallResult apply_execution(const RequestHandle &target, const ExecutionProposal &proposal, const CommittedExecutionFacts &facts, uint64_t &next_timeline_ordinal, bool open_activation_grant)
std::size_t retire_history(uint64_t through_ordinal)
Retire the journal prefix whose ordinals are <= through_ordinal.
void cohort_add(CohortHandle cohort, RequestHandle origin)
void visit_waiting_children(const RequestHandle &, void *, HandleVisitor) const
std::size_t history_base() const noexcept
How many events have been retired from the front of the journal.
Preparation< PreparedMutation > prepare_match_rejected(const RequestHandle &target, const EvaluationContext &context, MatchRejectReason reason, uint64_t &next_timeline_ordinal)
bool refresh_allowance(const RequestHandle &target, const EvaluationContext &context, const TargetObservation &observation, bool open_activation_grant)
refresh_allowance under the open activation grant of prepare_evaluation.
void refresh_point_allowances(uint64_t point, const PositionIdentity &position)
Consumer-only no-event form of the ordinary allowance refresh.
bool carry_book_read_failure(std::exception_ptr failure) const noexcept
A read of the book's spilled rows that failed where its reader cannot throw (a noexcept fold of the b...
bool refresh_cohort_allowance(const RequestHandle &target, const EvaluationContext &context, const TargetObservation &observation)
const LiveRequest & live_at(std::size_t rank) const
JournalView history() const noexcept
The command journal's retained window, oldest first.
friend const detail::ScratchReadback & journal_readback(const WorkingRequestCore &)
Preparation< PreparedMutation > prepare_match_rejected(const RequestHandle &target, const EvaluationContext &context, MatchRejectReason reason, std::optional< ExecutionTerms > attempted_terms, uint64_t &next_timeline_ordinal)
InstalledCommand< CancelResult > install_cancel(PreparedCancel &&prepared) noexcept
PreparedSubmit prepare_submit(const Request &request, const CommandContext &context, uint64_t &next_order_incarnation, uint64_t &next_timeline_ordinal, RequestOrigin origin=RequestOrigin::Host)
Consumer-only typed prepare/install.
Preparation< PreparedMutation > prepare_no_effect(const RequestHandle &target, const EvaluationContext &context, uint64_t &next_timeline_ordinal)
friend void bind_journal_codec(WorkingRequestCore &, detail::CommandScratchStore &)
InstalledCommand< ReplaceResult > install_replace(PreparedReplace &&prepared) noexcept
Preparation< PreparedMutation > prepare_bound_expiry(const EventId &physical_cause, const RequestHandle &child, const TargetObservation &observation, uint64_t &next_timeline_ordinal)
Preparation< Installed > apply_owner_applied(const EventId &applied, const RequestHandle &child, const std::optional< OpeningObservation > &observation, uint64_t &next_timeline_ordinal, const ArmContext &arm={})
WorkingRequestCore(RunIdentity identity, TailWindowConfig windows)
WorkingRequestCore(RunIdentity identity, CacheCapacity successor_roots, CacheCapacity receipts, CacheCapacity chain_segments)
Inclusive committed-row read-cache allowances.
Preparation< Installed > apply_evaluation(const RequestHandle &target, const EvaluationContext &context, const TargetObservation &observation, uint64_t &next_timeline_ordinal, bool open_activation_grant)
apply_evaluation under the open activation grant of prepare_evaluation.
ReplaceResult replace(const RequestHandle &target, const Request &request, int64_t decision_time_ms, uint64_t &next_order_incarnation, uint64_t &next_timeline_ordinal, std::optional< double > quantity_grid=std::nullopt, ReplaceOptions options={})
Preparation< Installed > apply_match_rejected(const RequestHandle &target, const EvaluationContext &context, MatchRejectReason reason, std::optional< ExecutionTerms > attempted_terms, uint64_t &next_timeline_ordinal)
void bound_close_handles(std::vector< RequestHandle > &out) const
Preparation< std::monostate > check_execution(const RequestHandle &target, const ExecutionProposal &proposal, uint64_t &next_timeline_ordinal, bool open_activation_grant)
check_execution and apply_execution under the open activation grant of prepare_evaluation.
GroupEffectReceipt group_effect_receipt(std::size_t index) const
Preparation< PreparedMutation > prepare_group_effect(const EventId &applied, const RequestHandle &recipient, uint64_t &next_timeline_ordinal)
detail::ReadThroughCacheStats tail_read_cache_stats(TailWindowKind kind) const noexcept
std::vector< RequestHandle > group_recipients(const EventId &applied) const
The live siblings an applied member's fill reaches – its group, another cohort, born before the fill;...
SubmitResult submit(const Request &request, int64_t decision_time_ms, uint64_t &next_order_incarnation, uint64_t &next_timeline_ordinal, std::optional< double > quantity_grid=std::nullopt)
R1 producer convenience: prepare then install one command.
Preparation< Installed > apply_group_effect(const EventId &applied, const RequestHandle &recipient, uint64_t &next_timeline_ordinal)
bool trigger_permits_driver(const Trigger &trigger, const TriggerState &state, DriverEligibilityClass driver_class, bool existing_matching_bit) const noexcept
bool has_waiting_children(const RequestHandle &parent) const
Preparation< PreparedMutation > prepare_trigger(const RequestHandle &target, const TriggerTransition &transition, DriverEligibilityClass driver_class, uint64_t &next_timeline_ordinal, std::optional< Side > cohort_side, const ActivationGrid &grid, bool open_activation_grant)
prepare_trigger under the open activation grant of prepare_evaluation: the cursor test every trigger ...
InstallResult install_mutation(PreparedMutation &&prepared) noexcept
static Allowance evaluated_allowance(const LiveRequest &live, uint64_t point) noexcept
The allowance that prepare_evaluation would install for this point.
InstallResult apply_execution(const RequestHandle &target, const ExecutionProposal &proposal, const CommittedExecutionFacts &facts, uint64_t &next_timeline_ordinal)
Preparation< Installed > apply_no_effect(const RequestHandle &target, const EvaluationContext &context, uint64_t &next_timeline_ordinal)
Preparation< PreparedMutation > prepare_owner_applied(const EventId &applied, const RequestHandle &child, const std::optional< OpeningObservation > &observation, uint64_t &next_timeline_ordinal, const ArmContext &arm={})
arm carries the materialization facts of an anchored leg (the price tick a rounded anchor snaps to,...
std::size_t cohort_receipt_count() const noexcept
Every cohort receipt this run committed, retained or not.
Preparation< PreparedMutation > prepare_trigger(const RequestHandle &target, const TriggerTransition &transition, DriverEligibilityClass driver_class, uint64_t &next_timeline_ordinal, std::optional< Side > cohort_side=std::nullopt, const ActivationGrid &grid={})
grid carries the run's activation grid (ActivationGrid); the default is the raw rule,...
CancelResult cancel(const RequestHandle &target, uint64_t &next_timeline_ordinal)
Preparation< PreparedMutation > prepare_parent_terminal(const EventId &terminal_or_replaced, const RequestHandle &child, uint64_t &next_timeline_ordinal)
std::size_t history_end() const noexcept
The absolute journal length: every event this run has committed, retained or not.
const std::map< std::uint64_t, CohortRoster > & nonempty_cohorts() const noexcept
Borrow the rosters holding origins, in ascending cohort-handle order.
uint64_t last_ordinal() const noexcept
The core's two durable counters: the ordinal of the last committed event and the last incarnation a c...
std::shared_ptr< const LiveRequest > hold_live(const RequestHandle &handle) const
find_live for a reader that keeps the row across further reads of the book.
const std::vector< CohortReceipt > & cohort_receipts() const noexcept
The retained cohort-receipt window, in command order.
Preparation< PreparedExecution > prepare_execution(const RequestHandle &target, const ExecutionProposal &proposal, uint64_t &next_timeline_ordinal, bool open_activation_grant)
prepare_execution under the open activation grant of prepare_evaluation.
void cohort_remove(CohortHandle cohort, RequestHandle origin)
Preparation< PreparedMutation > prepare_margin_call(const MarginCallEvent &event, uint64_t &next_timeline_ordinal)
Append one MarginCallEvent to the immutable history.
static bool effective_host_units(const PendingAdjustments &pending, double resolved_units, double *deduction, double *after, bool *exhausted) noexcept
Pure arithmetic over the cached pending total.
Preparation< Installed > apply_terms(const RequestHandle &target, const EvaluationContext &context, const TermsResolvedInput &input, uint64_t &next_timeline_ordinal)
void bind_scratch_slab(const std::shared_ptr<::pineforge::detail::ScratchSlab > &slab)
bool visit_prior_adjustments(const EventId &resolution, const std::function< bool(const EventId &)> &visit) const
The prior adjustment ids a TermsResolvedEvent or QuantityBoundEvent spent, in chain order,...
bool evaluation_eligible(const LiveRequest &live, const EvaluationContext &context, bool open_activation_grant) const noexcept
void rethrow_book_read_failure() const
Throws the failure carry_book_read_failure kept, if any.
PreparedCancel prepare_cancel(const RequestHandle &target, uint64_t &next_timeline_ordinal, CancelReason reason=CancelReason::User)
reason lets the kernel withdraw its own request under the durable Superseded receipt.
EligibilityFacts eligibility_facts(const LiveRequest &live, const EvaluationContext &context) const noexcept
void group_recipients(const EventId &applied, std::vector< RequestHandle > &out) const
The same three lists written over out, whose capacity a caller that keeps it reuses (R5 lane L3): the...
Preparation< Installed > apply_evaluation(const RequestHandle &target, const EvaluationContext &context, const TargetObservation &observation, uint64_t &next_timeline_ordinal)
Preparation< Installed > apply_trigger(const RequestHandle &target, const TriggerTransition &transition, DriverEligibilityClass driver_class, uint64_t &next_timeline_ordinal, std::optional< Side > cohort_side, const ActivationGrid &grid, bool open_activation_grant)
apply_trigger under the open activation grant of prepare_evaluation; every parameter is explicit,...
CommandInstalled< SubmitResult > apply_submit(const Request &request, const CommandContext &context, uint64_t &next_order_incarnation, uint64_t &next_timeline_ordinal, RequestOrigin origin=RequestOrigin::Host)
The direct forms (R5 lane L3).
CohortHandle cohort_open()
Command-boundary roster maintenance.
Preparation< Installed > apply_bound_expiry(const EventId &physical_cause, const RequestHandle &child, const TargetObservation &observation, uint64_t &next_timeline_ordinal)
std::vector< RequestHandle > bound_close_handles() const
std::variant< Flatten, order_action::Reduce, order_action::Transact > Action
Definition execution.hpp:17
std::variant< ExplicitUnits, OwnerOpenedUnits, ScopeFraction > ReductionSize
How much a Reduce takes: literal units, the owner's own opening, or a fraction of the bound scope.
std::variant< T, NoChange, PreparationError > Preparation
std::variant< PendingNone, PendingDeferred > PendingAdjustments
bool quantity_on_grid(double q, double step) noexcept
v2 exact binary64 grid: r=abs(q)/s, n=round(r) half away from zero, g=n*s.
Request market_request(execution::Action action, std::string label={}, std::string comment={})
bool has_market_defaults(const Request &request) noexcept
std::variant< Independent, WaitForApplied, BindOpening, BindOpenings, BindCohort > Owner
detail::ScratchStatus decode_journal_record(const detail::ScratchByteSource &, detail::ScratchBuffer, CommandEvent &)
std::variant< CashValue, EquityFraction > SizeBasis
The two bases a Sized intent may name.
std::variant< AcceptedEvent, RejectedEvent, ReplacedEvent, ReplaceRejectedEvent, CancelledEvent, NotWorkingEvent, InvalidHandleEvent, NoEffectEvent, MatchRejectedEvent, ExecutionAppliedEvent, CloseBoundEvent, ActivatedEvent, ReservationReducedEvent, DeferredGroupAdjustmentEvent, QuantityBoundEvent, ArmedEvent, TermsResolvedEvent, MarginCallEvent, NativeRiskEvent > CommandEvent
Every alternative of one request's history, as one value.
MatchRejectReason
Why a CANDIDATE was refused at a matching point.
@ UnrepresentableQuantity
The settlement's inspection answered execution::Status:: UnrepresentableQuantity: a close split binar...
@ RiskLimit
An opening refused while the run spec's generic risk limits are blocking (L9).
std::uint64_t opening_observation_count(const TargetObservation &) noexcept
ScopeClaim
Gross claims the whole bound scope; NetOfSiblings first subtracts the units already claimed by the li...
NativeArmVisibility
Whether an owner-related request that arms is a working order before its arm.
std::variant< Market, Limit, Stop, StopLimit, Trail > Trigger
SizePrice
WHICH price the basis is converted at.
std::shared_ptr< const RequestDefinition > DefinitionRef
std::variant< Flatten, Reduce, Transact, ReverseTo, HostSized, Sized > OrderIntent
What a request does.
detail::ScratchStatus freeze_openings(const RunIdentity &, const OpeningReadback &, std::shared_ptr< const OpeningReadback > &out)
Freeze and canonicalize a same-run, unique stream.
SizeTime
AtMatch resolves the basis at the matching candidate; AtAcceptance freezes the resolved units when th...
std::function< std::optional< double >( const LiveRequest &leg, const ExecutionAppliedEvent &owner_fill, Side leg_side, double offset, double kernel_level)> AnchoredLevelResolver
The one policy point of an anchored materialization.
ExecutionGridPolicy
Resolved host sizing normally remains subject to the run's quantity grid.
std::variant< NoGroup, Member > Group
bool operator==(CohortHandle left, CohortHandle right) noexcept
std::variant< PositionFlat, PositionNonflat > PositionIdentity
std::variant< CommandInstalled< R >, InstallError > InstalledCommand
std::variant< Absolute, FromOwnerFill > TriggerAnchor
RiskLimitKind
Which generic risk limit of NativeRunSpec::risk a NativeRiskEvent reports.
std::variant< ImmediateRemaining, PointBudget > Capacity
bool operator<(CohortHandle left, CohortHandle right) noexcept
RequestRejectReason
Why an acceptance was refused, on a Rejected SubmitResult.
@ PlacementAdmission
A Sized{SizeTime::AtAcceptance} whose acceptance-resolved quantity does not pass the run's opening ad...
std::uint64_t opening_count(const std::vector< RequestHandle > &, const OpeningReadback *) noexcept
JournalRecordSource encode_journal_record(const CommandEvent &) noexcept
ScopeBasis
Which measurement of the bound scope the fraction is taken of.
detail::ScratchStatus opening_observation_at(const TargetObservation &, std::uint64_t, OpeningObservation &) noexcept
void(*)(void *, const RequestHandle &) HandleVisitor
NativeAnchorRounding
How a materialized anchored level is snapped onto the run's price tick ladder (NativeRunSpec::price_t...
bool point_eligible(const LiveRequest &live, uint64_t point_ordinal, int64_t effective_time_ms) noexcept
Whether a request may be considered at this driver point: the point's ordinal must be strictly past t...
std::variant< AllowanceUnset, AllowanceUnits, AllowanceAllScope, AllowanceDeferred > Allowance
std::variant< BeginTrailTracking, ObserveTrailExtremum, ActivateStop, ActivateStopLimit, ActivateTrail > TriggerTransition
CancelReason
Why a request left the book.
@ Superseded
A kernel-originated request the kernel itself withdrew: the liquidation level or its units moved,...
@ UnrepresentableReservation
A group deduction's pending total that overflows binary64 (or a non-finite reservation,...
std::variant< execution::Book, execution::OpeningExposure, SelectedExposure > ExecutionScope
What one execution acted on: the whole book, one opening's exposure, or a bound selection.
NativeArmFirstMatch
When an armed request may first match.
std::variant< BookTransaction, Wait, ArmedTransaction, UnboundBookClose, BookClose, OpeningClose, OpeningsClose, CohortClose > Authority
std::variant< Installed, InstallError > InstallResult
ExecutionPlan to_execution_plan(const execution::Action &action)
detail::ScratchStatus append_journal_record(WorkingRequestCore &, const detail::ScratchByteSource &) noexcept
OrderIntent intent_from_physical(const execution::Action &action)
NativeCandidatePriceKind
Which price a candidate is being offered at: the point's own price, or the request's trigger level (a...
const detail::ScratchReadback & journal_readback(const WorkingRequestCore &)
std::variant< RemainingUnbound, RemainingFlattenAll, RemainingUnits, RemainingDeferred, NoTarget > Remaining
std::variant< RemainingProjectionUnbound, RemainingProjectionFlattenAll, RemainingProjectionUnits, RemainingProjectionDeferred, RemainingProjectionNoTarget > RemainingProjection
bool operator!=(CohortHandle left, CohortHandle right) noexcept
std::variant< execution::Flatten, order_action::Reduce, order_action::Transact, execution::ReverseTo > ExecutionPlan
The settlement shape one execution takes.
NativeArmScope
What an armed CLOSING request (Reduce, Flatten, a HostSized close) closes.
std::uint8_t display_waypoint(const MatchCursor &cursor) noexcept
void bind_journal_codec(WorkingRequestCore &, detail::CommandScratchStore &)
std::variant< EnrollmentFromCommand, EnrollmentFromApplied > Enrollment
std::variant< MarketReady, LimitReady, StopIdle, StopActive, StopLimitPending, StopLimitLive, TrailWaitArm, TrailTrack, TrailActive > TriggerState
order_action::Transact Transact
A signed book transaction: finite nonzero units on the side their sign names.
int r(int64_t c)
Definition color.hpp:42
Everything the kernel knows about WHERE a point is, as one owning value: its event ordinal,...
Where a request's trigger level comes from.
One request was accepted: the definition, and the timeline ordinal it was accepted at.
const RequestHandle & handle() const noexcept
What the consumer hands to prepare_trigger under an opted-in price grid (NativeRunSpec::price_grid ==...
What the consumer hands to prepare_owner_applied for an anchored leg's materialization,...
An anchored leg was materialized, exactly once: fill + offset, then the rounding, then resolve_anchor...
Bind to a host-built roster (cohort_open / cohort_add / cohort_remove), read at the match rather than...
Bind to ONE already-live opening, by its handle and the positive cycle it is live in.
Bind to a fixed cohort of already-live openings.
std::shared_ptr< const OpeningReadback > external_openings
The authority an armed leg acquires under NativeArmScope::Book: the whole position its owner's fill l...
Native sizing bases (L3).
A host-maintained, run-scoped roster identity.
std::vector< RequestHandle > origins
Canonical origin handles, ordered by origin incarnation rather than by host insertion order.
What acceptance needs from the run to judge a command: the point's ordinal and coordinate,...
bool units_are_scope_boundary
A Reduce's ExplicitUnits are a FIFO boundary of its scope: the binary64 sum, in book order,...
std::optional< double > sizing_scope
Placement-time sizing measurements the execution consumer owns, supplied only for the intent that ask...
std::optional< double > sizing_units
Kernel-resolved acceptance-time units for a Sized{AtAcceptance} request.
std::optional< double > price_tick
The run's price tick, needed only to resolve a tick-spelled trail offset or trigger anchor.
A group effect's deduction deferred into a sibling whose units are not known yet: its pending total g...
A fraction in (0, 1] of MARKED equity at the sizing point.
std::optional< Side > cohort_side
Set only while resolving a CohortClose candidate.
bool pre_open_birth_eligible
Generic current-point delivery.
Where an install put its events: first_index is an ABSOLUTE journal position – the count of events th...
One committed execution: the definition, the cursor, the resolved price and units,...
bool pre_open_birth_eligible
Carries the generic pre-open delivery authorization from the matching evaluation through the synchron...
A reduction of exactly these units, finite and positive.
Defer a level to the owner's fill: at the arm the level becomes fill + offset, with offset signed (ad...
One group-effect receipt: the applied event that caused it, the request it acted on,...
An opening or a closing whose quantity the HOST resolves, in NativeStrategyHost::resolve_execution_te...
Take whatever the point allows, with no per-point cap.
No owner: the request stands on its own book authority.
fill_through makes the limit a touch trigger (market-if-touched): the level still gates when the requ...
const std::optional< RequestHandle > & predecessor() const noexcept
uint64_t priority() const noexcept
Its place in the queue (RequestDefinition::priority): the handle's incarnation, unless a re-price kep...
std::optional< double > sizing_units
Placement-time sizing measurements frozen from the accepting command context.
const RequestHandle & handle() const noexcept
A kernel-issued liquidation that actually filled.
Match at the next eligible matching point, with no level to reach.
A candidate the run refused, with its MatchRejectReason and the cursor it was refused at.
A reduction or a flatten that had nothing to close: terminal, with no execution identity,...
const RequestHandle & handle() const noexcept
A dynamic cohort has no currently live member.
std::shared_ptr< const OpeningReadback > external_openings
A reduction of exactly what the owner's fill opened, bound at the arm and reported by a QuantityBound...
Cap what this request may execute at ONE matching point at units.
A working-request preparation that could not produce a representable instruction — a nonrepresentable...
A Reduce{OwnerOpenedUnits} was bound, at its owner's fill, to what that fill opened.
A resolved remaining quantity, in units.
Replacement behaviour that is not expressible in the successor request.
bool kept_handle() const noexcept
Whether the replace kept the handle (ReplaceOptions::keep_handle): the successor definition is the sa...
const RequestHandle & successor() const noexcept
const RequestHandle & predecessor() const noexcept
The immutable accepted form of one request: its handle, the request value as accepted,...
uint64_t priority
Where the definition stands in the run's queue: the number of the run's request sequence the command ...
std::optional< RequestHandle > root
The first handle of this request's replace chain – the request the chain began with,...
RequestOrigin origin
Appended last so every existing aggregate initializer keeps its meaning.
std::optional< PositionNonflat > kept_binding
The book binding a replace carried forward (ReplaceOptions:: keep_binding): the cycle and side of the...
Aggregate field order keeps market construction: Request{Transact{1.0}, "buy", "comment"}...
A group effect's deduction from a live sibling whose remaining units are known (GroupEffect::Reduce):...
Exact target exposure for an explicit reversal request.
A fraction in (0, 1] of the bound scope, resolved at the matching candidate.
ScopeBasis basis
Appended last so every existing aggregate initializer keeps its meaning.
Native authorization receipt, converted to a call-local financial SelectedOpeningSet only at the cons...
SizePrice price
Which price the basis converts at; see SizePrice.
ExecutionGridPolicy grid_policy
SnapToGrid floors the resolved units onto the run's quantity grid.
bool reserve_percent_fee
Divide the sizing cash by (1 + fee) when the run's fee kind is NativeFeeKind::Percent.
A stop that, once reached, becomes a limit at limit.
A stop trigger: the modeled path has to reach price from the adverse side.
Payload row policy for pending batches and committed-row read caches.
Storage observations only, excluded from continuation state.
A triggered trail; activation_ordinal carries TrailTrack's.
Trail offset spelled in price ticks instead of a price distance.
offset is a price distance: the stop rides offset behind the running best.
The one owner relation that arms (the ArmedEvent).
The authority an unarmed WaitForApplied child holds: accepted and live, but never a matching candidat...
How many spilled rows the book has read back to answer a lookup – by handle (find_live,...
std::size_t operator()(pineforge::native_order::CohortHandle value) const noexcept