Skip to content

R5 wave D: fifteen follow-up engine lanes E1–E15 integrated (INT13) - #273

Merged
luisleo526 merged 58 commits into
mainfrom
r5/int13-wave-d
Sep 22, 2026
Merged

luisleo526 merged 58 commits into
mainfrom
r5/int13-wave-d

Conversation

@luisleo526

@luisleo526 luisleo526 commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

What

R5 follow-up engine wave D: the fifteen gap lanes E1–E15 that fulfil what the two claimed-vs-actual audits, the docs lanes and the executed walk-throughs found, integrated onto the docs-wave main (9b50973). 53 lane commits + 3 integration commits (unions the lanes could not take, doc re-anchoring and citation fixes, floors from the rows that ran) + 2 test-only fixes for the Linux CI runners (INT13b: the receipt-skip row reads CTest's summary through the shared parser on both CMake wordings; the E14 witness pins its absent seed as same-host properties because native_continuation_hash folds the host's zoneinfo path — a finding for the next wave).

Lane Commits Content
E1 2 anchored trail: an absent arm is accepted (one-token validate_levels change), witness row
E2 2 equity/position extremes folded under every report policy except KernelRecordedAtHostMarks
E3 4 margin check converts FX at the check point's cursor (margin_check_fx, marked_equity_at, liquidation_level)
E4 4 tooling: macOS runtime gate on CPU time, runner modules -UNDEBUG, stale-binaries inputs, check_abi_receipt_skips.py --prepare
E5 3 adapter: offset trail arms on the tick-quantized path (source_trail_arm_level), settled by six NYSE:F TradingView tapes
E6 4 kernel comment residue deleted; entry-bar mask re-lowered behind the generic declare_opened_lot_entry_bar_mask seam
E7 4 typed C delivery words (pf_native_lookahead_e, pf_native_gaps_e), layouts pinned
E8 3 bracket receipt: per-leg BracketLegOutcome with state, result and reason
E9 4 adapter: one-shot sub-tick trail booking (source_one_shot_reach_tick, source_trail_reached_at), eight tapes
E10 6 detached-comment residue 539 → 0 over the kernel compile closure (comment-only, byte-proven); the never-written preceding_exit_path_prefix fold deleted with a type witness
E11 2 C spelling of the entry-bar mask (strategy_native_declare_opened_lot_entry_bar_mask_v1, C API 33 symbols)
E12 3 OrderBook::submit_or_replace_outcome / cancel_outcome surface the kernel's own results; old spellings are projections
E13 2 pf_native_working_v1 additive tail: a C caller tells an absent trail arm from an arm at 0.0; base-length callers still served
E14 5 kernel Trail::best_seed (rule-2 ruling row): the running best starts where the host says; C fourth request tail; adapter names each trail's activation; two shallow-next-bar tapes
E15 5 entry-bar mask follows the run's declared path order; dead PathOrderScope deleted; @host-seam census of base-class seams
integration 3 unions the lanes could not take (the open-lots example asserts the two-scope split, EXPECTED_NATIVE_C_API duplicate deleted, Doxygen cross-references fixed); doc re-anchoring + citation corrections (money-residue span 918-952); floors from the rows the integrated tree runs

Epochs unchanged (engine_script_run_v18, native_run_spec_v3, native_order_v6, PF_NATIVE_API_VERSION 1, PF_ABI_VERSION 4). Corpus pin 442d497 untouched.

Evidence

  • Every lane: supervisor fresh verify on all four profiles + full corpus parity 312/312 byte-identical + the 30-probe subset, before integration.
  • Integrated tree: preflight 25 stages (doc guards binding), release 566 (floor 566), debug 550, kernel 199 ran of 200 (floor 199), runtime budget ratio 10.4x (limit 15x); local macOS sanitizers blocked by the host (ASan wedge on exhausted swap — a 25-byte main never starts) — the Linux sanitizers CI job is the gate; kernel residuals 0 findings; rulings guard OK; C surface 55 members / 47 spelled / 8 excluded + 3 base-class seams, 33 C symbols; twin parity OK; doc anchors bad=0, lint 0, coverage 131/131, design inventory 0; Doxygen 0 warnings; parity 312/312 (excellent=311, anomaly=1) + subset 30/30.
  • Campaign sweep exp-r5-int13b-wave-d-20260922 on this head (6da97ac): execution pineforge-experiment-run-l4q9z, 72/72 cases, 0 failed; 4190 graded, 0 coverage lost; offline band1 +0/−0 band2 +0/−0 (score 0, hard regressions 0); pr_gate pineforge-pr-gate-gv72b → target.not-positive (neutral, accepted at net 0). The same result was measured on 9392273 (exp-r5-int13-wave-d-20260922, execution pnz7p) before the two Linux-CI test fixes (9200167, 6da97ac: tests/scripts only, src/include byte-identical).

The corpus cannot exercise E3/E5/E9/E14/E15's paths (on-grid 0.01 feed, no margin regime, AUTO order only); the population sweep above is their gate.

Merge with rebase.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Lg1KP7RXkwcEP2wFxXooy9

luisleo526 and others added 30 commits September 22, 2026 05:49
…th a placeholder (R5 follow-up lane E1, item 1)

`Trail{offset, std::nullopt}` under a `FromOwnerFill` anchor was refused with
`RequestRejectReason::InvalidTrigger`, so the anchored trail was the one
trigger kind whose placeholder had to be typed out: `Limit::price` and
`Stop::price` default to the `0.0` the anchored form wants, while
`std::optional<double>`'s own default is `nullopt`. Lane L14-B hit exactly
that assembling the six-feature host from the page alone, and the leg was
dropped while the rest of the strategy ran.

The ruling is that an absent arm is the anchored spelling, not an error, and
the tree already says so twice:

  1. `install_anchored_level()` assigns `trail->arm_price = level`
     unconditionally at the arm. It never reads the prior value, so the
     placeholder is write-only ceremony.
  2. An anchored request must be owned by `WaitForApplied`
     (`InvalidOwner` otherwise), so its authority is `Wait` until the arm.
     `eligibility_facts()` sets `facts.waiting` from that and the consumer's
     candidate scan skips a waiting request outright, so the pre-arm
     `arm_price` is unreachable by the matcher under either visibility.

So `validate_levels` now accepts an anchored trail with no `arm_price`. A
WRITTEN non-placeholder level stays refused for the reason a written
`Limit`/`Stop` price under an anchor is refused -- the arm would silently
overwrite it -- and `0.0` stays legal so every host already spelling it keeps
its meaning. No leg kind has an arm the anchor cannot resolve, so the refusal
that remains is a contradiction rather than an omission and needs no new
typed `SubmitError`: the C surface is untouched.

This is a generic capability, not a Pine one. The kernel learns that a
deferred field's absence is its own spelling; nothing here knows what a
source language calls a trailing exit.

The witness runs both spellings side by side, in batch and on a stream, and
pins that they arm at the same level and produce the same applied events and
the same trade. It also pins what does NOT converge: `hash_trigger()` folds
`arm_price.has_value()` and `continuation_hash()` folds the command history,
whose acceptance row keeps the definition as submitted -- so the spelling is
a durable identity choice with no behavioural consequence. That is why the
Pine adapter's placeholder (`relative_leg_shapes`) stays exactly where it is:
swapping it would move the continuation identity of every corpus run that
anchors a trail and buy nothing.

Floors: kernel 189 -> 190, release 554 -> 555 (one kernel-only TU).

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit a04d31a15200a0609bdf18ffc8f83708d5a7e60b)
…ens to take (R5 follow-up lane E1, item 2)

native-engine.md said the anchored field "carries the placeholder 0.0 until
then", which reads as a value to write and is what sent lane L14-B's assembled
host into an InvalidTrigger. The rule is the opposite: you do not write the
field at all, and each kind spells absence its own way -- the 0.0
`Limit::price` and `Stop::price` already default to, and either an absent
`std::optional` or that same 0.0 for a `Trail`'s `arm_price`. Writing any
other level is the error, because the arm overwrites it.

pine-to-native.md carries the same correction inside its existing bracket
row only, since lane L14-B is rewriting that page in parallel and the
integrator has to be able to carry this as one row.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit d0fec0adc5c3ce78e3ae4dc1c9de27e29fed3b23)
…(R5 gap lane E2)

A host that leaves `report_policy` at its `HostRecorded` default read
`max_drawdown_`, `max_runup_` and `max_contracts_held_all_/long_/short_`
back as zero for the whole run. Nothing was wrong with the fold: it was
simply never reached. `update_equity_extremes()` had exactly one caller,
`record_report_point`, and both ways into that helper were gated on a
report policy — `record_script_report_point` on `KernelRecorded` and
`mark_script_report_point` on `KernelRecordedAtHostMarks` — so the
kernel's own per-calculation folding happened only for a host that had
also asked the kernel to record its equity curve. Lane L14-B found it
from the outside, running one tape under both policies while writing the
open-lots example.

Ruling. Drawdown, run-up and the position peak are report truth of the
RUN: what it drew down, what it ran up, the most it ever held. They are
not a property of whoever owns the equity series, so the kernel folds
them whatever the report policy says. Recording the CURVE is the part
that stays policy-scoped — a `HostRecorded` host owns its series and
still gets no point from the kernel.

Where the fold happens is the policy's mark cadence, and there are only
two. When the kernel owns the cadence, the fold is at every script
calculation, which is what `HostRecorded` and `KernelRecorded` now
share; `KernelRecorded` additionally appends the point and, behind its
own switch, the broker-state hash row. `KernelRecordedAtHostMarks` is
the one policy that has handed the cadence to the host, and its fold
travels with it (`mark_script_report_point`), untouched: folding at the
kernel's calculation as well would count an instant the marking host
never reports — a different run truth, not a truer one. The Pine
adapter declares that policy, so not one of its numbers moves, by
construction rather than by tolerance.

One consequence is deliberate. The extremes are folded into the
execution continuation (`engine_state_hash.cpp`), so a `HostRecorded`
run's `native_continuation_hash()` / `broker_state_hash()` now moves
with them instead of folding three frozen zeros. A resume identity that
could not tell a run at its peak from a run at its trough was hiding
state; it no longer does.

Witness (`tests/test_native_report_truth.cpp`, kernel-only): one tape
through both policies, the extremes sampled at the top of every
calculation and read again at the end. They agree at every calculation
and at the end, they equal an independent walk of the curve the
`KernelRecorded` half recorded — which is what keeps
`compute_equity_stats` ("MUST mirror update_equity_extremes") one
statement under both — and the `HostRecorded` half still reports
`equity_curve_len == 0`. Before the change the `HostRecorded` column was
all zeros and the run diverged at calculation 12.

`scripts/check_broker_state_hash_coverage.py` pins the recorder's rows.
expectation corrected:
`if (!spec || spec->report_policy != NativeReportPolicy::KernelRecorded) return;`
-> `if (!spec) return;` plus a separate
`if (spec->report_policy != NativeReportPolicy::KernelRecorded) return;`
below the now policy-independent `engine.update_equity_extremes();`,
because one combined guard cannot express two scopes. The fold itself is
added as a pinned row, so re-gating it fails that gate closed.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 9b5a8f127c4f077d56acfceefa35a9a37ffab50c)
docs/pages/native-engine.md's "Reporting for native hosts" said the curve
is host-owned by default and left the reader to conclude that everything
downstream of it is too. Two things hang off that default and they do not
share a scope: the `pf_equity_stats_t` figures are a walk over the
recorded curve and do degenerate without one, while `max_drawdown_`,
`max_runup_` and `max_contracts_held_all_/_long_/_short_` are scalars the
kernel folds at every script calculation under every policy. The section
now names the split and says where each is read back; the `KernelRecorded`
paragraph describes what it adds (the point) rather than re-claiming the
fold, and points at the mirror the two halves share.

docs/pages/pine-to-native.md gains the two rows a migrating script looks
for — `strategy.max_drawdown` / `strategy.max_runup` and
`strategy.max_contracts_held_*` — with the same distinction: the scalars
need no report policy, the `pf_equity_stats_t` pair needs a curve. Lane
L14-B is rewriting this page in parallel and states the pre-E2 rule (zero
under `HostRecorded`); at integration its sentence is the one to drop.

The engine.hpp NOTE above `fold_equity_extreme` is rewritten in place at
its original eight lines, so the ~40 `engine.hpp:NNNN` citations that
docs/design, docs/adr and docs/pine-adapter-kernel-notes.md carry past it
stay valid.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 759c6e66444d3c9d0b70a643c5f43596722c5b48)
…up lane E3, item 1)

Ruling: the account-currency rate one margin check point converts at is the
rate the run's declared curve has in force at THAT POINT'S cursor — the
curve's own step semantics, `account_currency_fx_at(cursor.point
.effective_time_ms)` — for every check kind (BarOpen, AfterApplied,
Calculation, FxRoll) and in batch and stream alike. This is generic: it names
a declared FX curve and a check point, no script language and no broker.

The margin model was the last thing in the kernel still converting at the
PRESENTED bar clock. That clock is a different instant whenever the walk has
moved past the point being checked: an applied fill drained at its script
bar's calculation is presented that bar's close coordinate
(`invoke_applied_callback` sets `max(cursor, decision_floor())`) while its
cursor still stands at the opening print it filled on. A curve step landing
between the two made the entry fill itself breach — lane P4 hit exactly that
writing the C fx-roll scenario ("required 1250 at cursor time 300000, where
the curve's own step was at 600000") and moved its step one bar later to dodge
it. Every OTHER cursor-converting site in the kernel already threads the rate
from the coordinate: a `Sized` request freezes its units at
`account_currency_fx_at(point->decision.coordinate.effective_time_ms)`, an
execution term records `active_fx` at `cursor.point.effective_time_ms`, and
`consume_matched_request` states the rule outright ("All resolver, inspection,
admission and financial reads see the execution coordinate's FX activation
instant"). The margin model now joins it.

Mechanism — the rate is threaded, never the clock. `margin_check_fx(engine,
cursor)` answers one point's rate; `margin_call_units` takes it for the
requirement and hands it to `margin_equity`, `maintain_margin_liquidation`
solves the level with it, and the margin-call receipt states the equity,
requirement and level of the book it left behind at the rate where the fill
that booked it landed. `marked_equity_at` is `BacktestEngine::marked_equity`
term for term at an explicit rate, because both terms of the test are
FX-bearing: reading the later clock invented equity as well as requirement.
No host callback sees a clock it did not see before, and no new durable state
exists, so no continuation identity and no epoch decision move.

N6's local clock swap inside `fx_roll_margin_check` is removed: with the
generic rule in place the roll's own instant IS the instant it is measured at,
so the swap was exactly redundant — `test_native_margin_fx_roll` (844 checks)
and `test_native_fx_activation` (180 checks) are unchanged and green.

N6's FxRoll DEFINITION is kept verbatim — the first driver point whose rate is
not its predecessor's in the driver log — because it was already stated in
cursor time; it is now the same rule the checks it precedes are taken under,
instead of a special case beside them.

`native_liquidation_price()` keeps the presented clock: it is a host query
with no cursor of its own, which is what "where is my level now" means.

Adapter: `src/source` declares no kernel FX curve of its own, but generated
Pine calls `strategy_set_account_currency_fx_series`, which stages one
(`stage_account_currency_fx_series`), and the adapter declares a
maintenance-only kernel margin model whenever the margin call is enabled — so
the two do meet. Its outcomes still cannot move: its model is
`PathAdverseExtremeMark`, which never solves `liquidation_level`;
`resolve_margin_requirement` replaces both of the kernel's numbers with
`source_margin_money(view.mark, view.cursor.point.open_ms)`, already keyed on
the cursor; and `resolve_margin_call_units` always answers, so the kernel's
restore is discarded. The only kernel number left is `unit_margin`, read as a
finite-positive gate whose truth a validated positive rate cannot change.

Witness `tests/test_native_margin_fx_clock.cpp` (607 checks), kernel-only —
P4's geometry with the step AT the entry bar's presented clock, batch and
stream, both terms of the test, every kind on its own cursor, the receipt, and
a no-curve run left untouched. Fail-before on this tree: 10 failures, the
applied check measuring `required 1875` where the cursor's rate owes 750 and
calling a margin at T+1m that the account never owed.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 1e64e19c77039ed5152fc6a9ac544aeae4302e63)
…ed it (R5 follow-up lane E3, item 2)

Lane P4 wrote `FX_ROLL_STEP_MS 900000` and said so in its report: its first
version put the step at 600000 and watched the ENTRY fill breach at
`required 1250` with its cursor at 300000, so it moved the step one bar later
to get the scenario's own acceptance geometry back. That was a kernel defect
being dodged by a test literal, not a geometry choice.

expectation corrected: FX_ROLL_STEP_MS 900000 -> 600000, because 600000 is bar
1's close coordinate — the clock the engine presents while the entry fill's
applied check is drained — and with item 1 in place the margin model converts
at that check's own cursor (300000, rate 1.0, required 500 against equity
1000) instead. The entry is quiet, and the roll at 600000 is what breaches,
which is what the scenario's hand arithmetic describes.

Every existing C row is unchanged and still passes: one FxRoll offered, its
cursor at the step, `equity 1000 / required 1250`, one margin call of 2 units,
one closed row under the declared ticket at entry price 100. One row is added
— `call_time_ms`, read from `pf_native_event_v1::effective_time_ms` — to pin
WHICH point called, which is the whole difference the two geometries expose.

Fail-before with the kernel at 0a47cbf and this file as committed: 3
failures, "the roll named another point (got 0, want 1)", "the roll's two
numbers are not the new rate at the unchanged price (got 0, want 1)" and "the
margin call was not made at the roll's own instant".

No C surface change: no symbol, struct, enumerator or layout moves.
`scripts/check_native_c_api_surface.py` and `scripts/check_c_abi_runtime.py`
both exit 0, and `test_native_c_api_frozen_header` still builds and passes.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 312947cce3762938caf483d1d97ce9a38cac3547)
…efusal is not about it (R5 follow-up lane E3, item 3)

`native-engine.md`, "Margin and liquidation": the `equity(P)` / `required(P)`
block named an `fx` and never said which one. It now says: the rate of the
check point being taken, `account_currency_fx_at(cursor.point
.effective_time_ms)`, on the declared curve's own step semantics, for both
terms of the comparison and for the level, at every check kind, in batch and
stream. The presented bar clock is named as what it is — a later instant
whenever the walk has moved past the point — with the applied-fill case that
exposes it, and `native_liquidation_price()` is stated as the one query that
answers at the presented instant because it has no cursor of its own.

The FX-roll paragraph gains the sentence that makes N6's detection and E3's
conversion one rule rather than two: a point's rate is read at its own
effective time, so "the rate the next match will use differs from the one the
last point used" is already stated in the cursor time the check is taken in.

"Stream FX" — the tick refusal's reason is corrected in substance, not
loosened. E3 asked whether that refusal existed only because of the stale
clock; it does not. The clock it names is `active_account_currency_fx()`,
which is what every SCRIPT-VISIBLE conversion reads — `open_trade_profit()`,
`marked_equity()`, the equity curve — and the observation hook runs before its
print has moved it. Re-measured on this tree with the refusal temporarily
lifted: a step at T+90s left the T+100s and T+110s prints converting at the
pre-step rate, one whole slot behind. The margin model is now correct on that
route; the money the script reads there still is not. The refusal stays, and
`tests/test_native_margin_fx_clock.cpp` pins both refusal messages.

`native-feature-parity.md`: MG9 records E3 completing the FX margin row's
conversion instant, FP6 records the tick refusal as re-examined and kept with
the reason it actually guards.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 5bc703d7bb5b1f470739ef42c3f6c0487442b686)
… own accessor (R5 follow-up lane E3, item 4)

`NativeExecutionConsumer::marked_equity_at` restates
`BacktestEngine::marked_equity` (engine_execution.cpp) term for term at an
explicit rate, and item 1's comment claims the two are pinned together. They
were not: nothing in the witness compared them. They are now.

`ClockHost::resolve_margin_requirement` records
`BacktestEngine::marked_equity(view.mark)` — the accessor, converting at the
engine's presented clock — and that clock, beside every measured view. Two
rows read it:

- a run with no declared curve, where the two clocks are the same rate at
  every instant: every view's equity must be the accessor's number bit for
  bit (`==`, not a band);
- every curve run in "every kind converts at its own cursor": the same
  identity wherever the presented clock's rate happens to be the cursor's,
  which is where the rule is a no-op by construction.

Mutation check that the pin bites: multiplying the unrealized term in
`marked_equity_at` by `1.0000001` — a 1e-7 relative drift, far below any
tolerance a `near()` band would catch — fails 11 checks, all of them
`host.views[i].equity == host.accessor_equity[i]`. Reverted; the witness is
628 checks, 0 failures.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit a70b6a2af84c737eee57290d94858833d4caae92)
…, item 1)

Lane L14-C read the next non-comment line after every TradingView-shaped
comment in the kernel's compile closure and found four spans with no code
inside them at all: 312 lines describing functions the R5 lowerings deleted.
A documentation lane may not touch src/, so the text stayed. It is deleted
here, and each span is replaced by one sentence naming the live generic
mechanism and the ADR row.

Verified on this tree first: the three files are byte-identical to r5/L14C's,
so its line numbers apply verbatim.  Each span re-read; each named "next
code" confirmed.

  src/engine_orders.cpp:200-209   sibling reduction on a group fill
      -> native_order::GroupEffect (Cancel / Reduce), resolved by the
         execution consumer.  Next code was build_close_trade_with_costs.
  src/engine_orders.cpp:530-692   the whole "execute_market_entry case
      helpers" section -- deferred-flip growth, the opening margin guard,
      same-tick sequential fill.  Next code was `}  // namespace pineforge`.
      -> a frozen pending-row field name; NativeMarginModel::initial_long /
         initial_short; the consumer's acceptance and incarnation ordinals.
  src/engine_run.cpp:148-167      the chart-body warmup scope and the
      per-bar dispatch order.  Next code was reset_run_state().
      -> the ambient ta::EmaSeeding default the source layer raises;
         NativeCloseExecution (AfterCalculation / NextEligiblePoint).
  include/pineforge/engine.hpp:918-952   ten-significant-digit money
      -> the source adapter's own arithmetic; the kernel's money is exact.
  include/pineforge/engine.hpp:1215-1379  the sizing freeze, the fill-recalc
      sizing moment, the post-liquidation re-freeze, the close-time position
      freeze.  Next code was net_profit().
      -> SizeTime + SizePrice; NativeLiquidationCheck; NativeCloseExecution.

Two boundaries were taken wider than L14-C's table cites, both inside the
same dead region and for the same reason -- what is left would label nothing:
engine_orders.cpp from :530 (the section banner and the first helper's
comment) rather than :556, and engine.hpp from :1215 (the on-tick sizing
census the freeze cites) rather than :1262.  Kept, because they document live
declarations: the "Shared close-side" banner and build_close_trade_with_costs'
own doc, round_to_mintick's and bar_fill_price's census comments, the
"Strategy variable accessors" banner, reset_run_state's doc.

Ruling: the kernel documents the code it has.  A pointer to the generic
mechanism is documentation; a pinned narrative about a deleted function is a
false statement about this tree, and rule 5's "legacy to extract" is, for
these spans, text and nothing else.

Comment-only, proved by construction: stripping every comment from each file
at both revisions and dropping blank lines leaves the code byte-identical
(3 files, sha256 equal), and `git diff -U0 -- include src` has 0 non-comment
lines.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit a191fda9aa3881e3d2f2351510f1eb9232c2b47a)
…l derives the mask (R5 lane E6, item 2)

`PyramidEntry::skip_entry_bar_high` / `_low` were ADR-0001 rule 5's last live
pair of kernel state written by the source adapter: `pine_strategy_host.cpp`
reached into the kernel's own lots and set two booleans, having first computed
them from two kernel internals it had included for that purpose
(`internal::first_touch_position`, `internal::bar_path_uses_high_first`).

The split this lane makes is the one rule 2 asks for. What the adapter knows
is not generic — whether ITS order was a priced entry, and therefore whether
the fill sits inside the bar or at its closing point — and it keeps that.
What the kernel knows is the geometry: which intrabar leg the modeled path
walks first and where a price is first touched on it. So the owner of the
lot's excursion (RULING A48, `owns_lot_excursions`) now DECLARES the fill
point and the kernel DERIVES the flags:

    enum class OpenedLotFillPoint { OnPath, AfterPath };   // engine.hpp
    void BacktestEngine::declare_opened_lot_entry_bar_mask(
            uint64_t entry_incarnation, const Bar&, OpenedLotFillPoint);

It is the entry-side half of the capability whose read side already crossed
the boundary generically (`ClosedLotExcursionFacts::entry_bar_high_masked`),
and it names no platform: any host that samples its own lots says where its
fill sat, and a host that forces the leg order still does it once, through
`NativeRunSpec::path_order`. The adapter's two writer sites are now two calls;
no host writes a lot's flags.

Ruling: generic mechanism (rule 2a — a second venue is driven through this
surface by answering OnPath / AfterPath), and no knob (rule 2b — the geometry
is a decision the run has already made, in the matcher and in the closing
row). Storage is unchanged BY DESIGN, and that is the residual: the two
booleans are durable, hashed lot state (`src/engine_state_hash.cpp:65`) inside
a POD the settlement ABI freezes, so removing them is an epoch decision and
this lane does not take one. What is removable at the next epoch, and how, is
recorded in ADR-0001.

Witness, kernel-only and source-free (runs in the kernel profile):
tests/test_e6_entry_bar_mask_declaration.cpp — 7 runs over two entry-bar
geometries. The same declaration masks the HIGH on a high-first bar and the
LOW on a low-first one, because the geometry is the bar's; AfterPath masks
both; declaring another incarnation's mask leaves this lot clear; and the
mask drives the skip, the owner's magnitudes and the closed row's differing
by exactly the masked end (mfe 12 -> 0, mae 12 -> 0).

Fail-before against this branch's header closure at a191fda9, verbatim:

    tests/test_e6_entry_bar_mask_declaration.cpp:106:47: error: use of
    undeclared identifier 'OpenedLotFillPoint'

After: `test_e6_entry_bar_mask_declaration: 161 passed, 0 failed`.

Byte-identical by construction: the derivation is the adapter's former helper
moved unchanged (same predicate, same per-lot fill price, same "a fill the
path never reaches leaves the lot alone" arm), called at the same two sites in
the same order.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 4e048ee3bbbbb8b3c5d6fea8dfc20606433f463e)
…ane E6, item 3)

ADR-0001 on this branch predates lane L14-C's rewrite, so this is the smallest
edit that keeps it true, and it is exactly the two rows the lane moved.

1. Rule 5 ("Boundary rules") now records what it still covers. The comment
   residue it covered is deleted -- the four spans L14-C tabulated, cited at
   0a47cbf so the reader can find them in history -- and the last kernel
   state a host wrote, PyramidEntry::skip_entry_bar_high / _low, has no writer
   outside the kernel. What rule 5 still covers is the STORAGE of that pair.

2. A second row in "Kernel state the adapter sets -- the mechanism rulings",
   beside ta::ema_na_warmup_flag: the mask pair, its new declaration seam, its
   rule-2 argument on both halves, its two pinning suites, and the exact
   mechanism that removes it at the next epoch (the owner keys its own tables
   by entry_incarnation already, and the kernel's own fallback reader behind
   `preceding_exit_path_prefix` has no writer anywhere in the tree, so it is
   unreachable in every run).

The row adds four identifiers to the residual gate's first-column vocabulary
(`skip_entry_bar_high`, `skip_entry_bar_low`, `PyramidEntry`,
`ClosedLotExcursionFacts`); none matches a residual pattern, so the 106
vocabulary-matching identifiers the gate enforces are unchanged.

For the integrator carrying this onto r5/L14C, which rewrites the same file:
its section A table (`:468-490`) becomes "deleted by lane E6" and its
consequence bullet (`:595-598`) loses the "312 lines" claim; its section B row
for the pair (`:510`) and its "Current state vs. target" bullet (`:274-277`)
take the new writer. Both of this lane's edits are the same two facts.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit bb64f2ae54e6e0ab667bbba4bd299988e65c0f10)
tests/test_e6_entry_bar_mask_declaration.cpp registers in both gated
profiles: it is source-free, so the kernel profile builds it too.

expectation corrected: KERNEL_MIN_TESTS 189 -> 190, because the kernel
profile now registers 191 rows and runs 190 beside the same skipped
test_native_live_websocket.

expectation corrected: RELEASE_MIN_TESTS 554 -> 555, because release
registers 555 rows and, with --require-receipts, runs all of them.

An integrator merging this lane beside another that also adds rows adds both
deltas: E6 contributes exactly +1 to each floor.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit b2dda9a6e29316412f3a69c3fb184e74e81eb9e3)

# Conflicts:
#	scripts/ci_verify.py
…nes do not

Lane E6 measured 524 detached comment lines left in the kernel's compile
closure and reported the number without a tool. A number no one can re-derive
is not a measurement, so this is the measurement: within one gap between two
code lines the last comment block is attached to the code below it and every
earlier block in that gap is detached -- it is separated from the next
declaration by a blank line and another block, so nothing in the file says
what it is about.

One class is excluded, because its position is not a claim about the code
under it: a DIRECTIVE block, whose text a tool consumes. A Doxygen group
command (@defgroup / @addtogroup / @name and the @{ @} that open and close a
group) brackets a run of declarations, so the closer necessarily follows the
last of them; the @broker-state / @source-state markers that
scripts/check_broker_state_hash_coverage.py reads must sit exactly around the
members they enclose. Moving either breaks the tool that reads it, so counting
them as residue would make the census ask for a defect.

The scope is the kernel's own compile closure, read out of CMakeLists.txt
(PINEFORGE_KERNEL_SOURCES) and followed through #include: the kernel is the
half that has to stand alone, and the source adapter is judged elsewhere.

The classifier is structural, not lexical-by-regex: it tracks block comments,
string and character literals and raw strings, so "http://" and R"(/* x */)"
are code, and a line carrying any code is code even when it also carries a
comment. --self-test pins that behaviour on eighteen fixtures, including the
two that a regex gets wrong.

It judges position only, never truth. Deciding what a detached block should
become -- deleted, moved onto its declaration, or kept -- is reading work.

Ruling: generic. The script names no Pine or TradingView concept; it measures
C++ comment position in whatever files it is given.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 92f24d61c6b8fe7cefde80e335e16dffd31d37bb)
…rong line is moved (R5 lane E10, item 1)

The census this lane commits reads 539 detached comment lines in the kernel's
compile closure after lane E6 (engine.hpp 325, engine_orders.cpp 100,
engine_run.cpp 34, timeframe.hpp 27, pineforge.h 17, plus ten files under ten
lines). After this commit it reads 0. Every block was read; each of the three
verdicts was earned.

DELETED -- a narrative about a mechanism this tree no longer has. Every name
below survives only inside the comment that described it: the input.source
resolver, the one-priced-entry-per-bar latch, the entry-request tombstones and
the cancelled-incarnation map, both request-matching scratchpads, the
dual-entry arbitration pair (dual_entry_path_, last_bar_dual_entry_decision_),
the trail-restart bar and its pre-bar extreme, the whole forced-liquidation /
margin-call method section (engine.hpp:806-917), the intraday-loss rule,
the five market-entry case helpers, the market exit, the three partial-exit
helpers, the range-end accounting essay, the exposure-activation note, the
run() helpers, the per-script-bar dispatch sequence, run_tf_impl, and the
hundred-odd lines of engine.hpp that restated the C surface's own
documentation of the live-runtime observers. Where a reader of the file would
otherwise be left without a next step, the deletion leaves one sentence naming
the live generic mechanism -- in the file's own banner, which is where a
file-scope statement belongs and where nothing can orphan it again.

MOVED -- still true, on the wrong line. Section banners separated from the
declaration they label by a blank line and another block are fused with it
(timeframe.hpp, session_time.hpp/.cpp, native_matching.hpp, native_c_api.h,
ta.hpp, ta_misc.cpp, ta_moving_averages.cpp, ta_extremes_volume.cpp,
c_abi.cpp, pineforge.h). native_calendar.hpp's component note moves onto the
namespace it describes. pineforge.h's "STRATEGY .SO EXPORTS" banner moves onto
the #ifndef PINEFORGE_NO_STRATEGY_DECLS block it actually introduces, from
above the runtime export that had drifted in front of it. E6's own pointer
sentences move from the vacuum they sat in onto the thing they bound: the
money sentence onto round_to_mintick, the three class-scope ones onto the
accessor section, the two file-scope ones into engine_run.cpp's banner. The
live-runtime observers get one generic doc: the kernel answers "not
available", the host that owns the book overrides, the C header says what
each value means.

KEPT and CORRECTED -- documents live code, named something dead. Three
citations created by the same lowerings: range_end_trades_ cited a deleted
engine_orders.cpp function (the rows are the execution consumer's, under
NativeRunSpec::report_open_position_at_end); the session predicates cited a
setter that is the adapter's; the observers' behaviour is the adapter's, so
the kernel states the seam and not the behaviour.

Two comment classes are NOT residue and were not touched: Doxygen group
commands and the @broker-state region markers, whose position belongs to the
tool that reads them. The census excludes them for the same reason.

Ruling: generic, and comment-only. The kernel now documents the code it has,
in the vocabulary of the mechanism it has -- no TradingView tape, probe or
family letter explains a kernel declaration that no longer exists. Proof, per
lane E6's method: for each of the fifteen files the blank-normalized
comment-stripped bytes are unchanged, so the preprocessor sees the identical
translation unit.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 8a5273a9a10c69b5dee1dd4279aeedffda9381f7)
`execution::PhysicalExecutionContext::preceding_exit_path_prefix` was read in
exactly one place -- the non-hook excursion fold of
BacktestEngine::build_close_trade_with_costs -- and written nowhere: not in
src/, tests/, examples/, runner/, tools/, benchmarks/, tutorial/ or the source
adapter. A std::optional<bool> nobody assigns is empty at every call, so the
twenty-one lines behind it never ran in any run this engine has performed.
Lane E6 measured that and recorded it as a finding; this deletes the member
and its reader.

Deletable by the boundary's own test. PhysicalExecutionContext is a plain
stack-bound settlement coordinate in the unversioned pineforge::execution
namespace: no field of it is hashed (engine_state_hash.cpp names none of it),
it is in no frozen layout the settlement or aggregate ABI checkers pin (it
appears in neither check_native_cpp_versions.py's FILES nor either ABI
pairing's caller sources, whose own compiles each see one epoch's headers),
and it is nowhere on the C surface (pineforge.h, native_c_api.h, c_abi.cpp).
Nothing durable, frozen or C-visible changes, so nothing here asks for an
epoch decision. The sibling carry preceding_exit_trail_peak, which does have
writers, is untouched.

The witness, tests/test_e10_dead_path_prefix.cpp (kernel-only):
  * the type witness is the proof of unreachability -- after this commit no
    translation unit CAN set a path prefix, so a writer anywhere would have
    failed to compile, and the whole-tree build is that check. Fail-before
    against the header closure at 8a5273a9:
      tests/test_e10_dead_path_prefix.cpp:70:15: error: static assertion
      failed ...: PhysicalExecutionContext still carries the dead
      preceding_exit_path_prefix member
  * the behaviour witness answers why it could sit unwritten. The fold
    existed for the extreme a priced exit's mid-bar fill leaves behind, on
    the theory that per-bar sampling never sees it. It does see it: on a
    high-first bar whose high is 8.00 above the lot, a stop exit's closed row
    reports favorable 16.00 on two contracts whether the lot opened on an
    earlier bar or on the exit's own bar. The branch covered nothing.
  * the sibling witness keeps the trail carry's fold pinned beside it.

Six test files spelled the empty prefix as a fourth aggregate initializer and
now spell three fields. No expectation moves: every one of them was `{}`.

Ruling: generic and behaviour-neutral by construction. The corpus run proves
the outcomes byte-identical; the type witness proves the branch could never
have contributed to them.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 3a1bbb67d767366d8c614f6bc2abe67ab56fd5c3)
Row 1 -- "Current state vs. target", the ten-significant-digit money rule
(line 206 at 3a1bbb67). It cited `engine.hpp:73-180`, which is and was live
code: ClosedLotExcursionFacts, PyramidEntry and Trade with their own docs. The
money residue was `engine.hpp:918-952`, 35 lines between round_to_mintick and
bar_fill_price, and lane E6 deleted it. Lane L14-C's residue table carries the
same wrong span in its row 8 and in the same bullet on the docs branch
(r5/L14C and r5/int12-docs, `docs/adr/0001-kernel-adapter-boundary.md:265`
and the table row at `:483` / `:525`, plus
`docs/pine-adapter-kernel-notes.md:16`); this repository's copy is corrected
here and the integrator re-applies the same correction to the rewritten table.

Row 2 -- the entry-bar mask pair, in "Kernel state the adapter sets". Lane E6
wrote that the kernel's own fallback reader of the pair has no writer and is
therefore unreachable in every run. That is still why the pair needs no kernel
replacement at the next epoch, but the fallback itself is now deleted (item 2
of this lane), so the row says so and names the witness.

Neither row changes a ruling: the pair is still retained, still hashed, still
removable only at an epoch.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit af49badeea39d8bde03780e4254dbb6630640501)
tests/test_e10_dead_path_prefix.cpp is source-free -- it binds
pineforge/native_host.hpp and nothing under pineforge/source -- so it
registers in the kernel profile as well as in release.

expectation corrected: KERNEL_MIN_TESTS 190 -> 191, because 192 rows now
register in the kernel profile and 191 run beside the same skipped
test_native_live_websocket.

expectation corrected: RELEASE_MIN_TESTS 555 -> 556, because the same row
registers in release, where nothing skips.

Both floors were exactly tight at this lane's base (E6's tip), so an
integrator merging this lane beside another row-adding lane adds both deltas.

Also here, because the full build is what found it: the SEVENTH aggregate
initializer of a PhysicalExecutionContext that spelled the empty path prefix,
tests/test_source_layer_native_defaults.cpp:34, whose multi-line form the
item-2 sweep missed. Item 2's message says six files; it is seven. No
expectation moves -- the dropped initializer was `{}` there as everywhere --
and the miss is exactly the compile-time proof that commit relies on doing
its job: a translation unit that still named the member failed to build.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit fcfb89a2ed6cc22a44fd32865ba789815e278b66)
Item 1's new doc for the live-runtime observation seam listed the C spellings
a reader should follow, and one of them -- `strategy_probe_fill_qty` -- is not
a symbol this project has. The C surface spells it
`strategy_pending_order_fill_qty` (include/pineforge/pineforge.h:1015), beside
the five names the same sentence gets right.

Found by reading pineforge.h rather than by trusting the C++ member name the
deleted residue had used. A pointer sentence whose symbol does not exist is
the same defect the lane deletes, one indirection further out.

Comment-only: the blank-normalized comment-stripped bytes of engine.hpp are
unchanged by this commit.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 704c0ca3d352b9c02661501dfd5b4e645f6f07e6)
…t (R5 lane E11, item 1)

Lane E6 cut the entry-side half of RULING A48 as a protected kernel seam,
BacktestEngine::declare_opened_lot_entry_bar_mask(entry_incarnation,
entry_bar, OpenedLotFillPoint), and recorded (exec/E6-final.md, finding 4)
that a C host could own its lots' excursions (on_lot_excursion) but had no
way to declare the mask its closing facts carry. This is that spelling:

    typedef enum pf_native_opened_lot_fill_point_e {
        PF_NATIVE_OPENED_LOT_FILL_POINT_ON_PATH    = 0,
        PF_NATIVE_OPENED_LOT_FILL_POINT_AFTER_PATH = 1
    } pf_native_opened_lot_fill_point_t;
    PF_API int strategy_native_declare_opened_lot_entry_bar_mask_v1(
            pf_strategy_t, uint64_t entry_incarnation,
            const pf_bar_t* entry_bar, uint32_t fill_point);

The typed word is pinned to OpenedLotFillPoint by static_assert and read
by an exhaustive switch (PF_NATIVE_E_TAG outside it); a NULL bar is
PF_NATIVE_E_ARGUMENT, a handle this API did not create PF_NATIVE_E_HANDLE.

Legality mirrors the one frame the seam is called from: the source adapter
declares from on_native_applied, where a host learns that a fill opened a
lot, and so does the E6 witness. CCallbackHost marks the frame of its C
on_applied call (AppliedFrame: saved and restored, so a nested applied
execution cannot close it, and restored on the CallbackFailure throw), and
in every other frame the call answers PF_NATIVE_E_STATE -- the refusal the
other C calls answer where they are illegal -- and changes nothing. The
mark is host-side transport state like the two snapshot caches: never
durable engine state, never hashed.

Ruling: generic mechanism, no knob. The C spelling carries exactly the C++
member's one fact, where the fill sat; the kernel keeps the geometry, and
nothing names a platform. No epoch: a symbol and an enum are added,
PF_NATIVE_API_VERSION stays 1 and no layout moves. The source adapter is
not touched, so the corpus is byte-identical by construction; this lane's
parity sweep measures it.

C API SOP:
  - scripts/check_c_abi_runtime.py: EXPECTED_NATIVE_C_API 32 -> 33.
  - scripts/check_native_c_api_surface.py: unchanged, and it must be. Its
    census is the public surface of NativeStrategyHost; the seam is a
    protected member of the base, so the guard neither requires nor admits
    a row. A [C] row naming it fails as "COVERAGE rows naming members that
    no longer exist: declare_opened_lot_entry_bar_mask" (measured on a copy
    of the header). The COVERAGE block records it in prose beside the
    pf_trade_t asymmetry, the one form the guard admits.
  - SCOPE, the callback table's legality paragraph and the lot-excursion
    facts' doc name the new call.

Witness: the entry-bar mask scenario of tests/test_native_c_api.c, pure C.
One run, two lots -- two units at the close of the high-first bar
(O100 H101 L90 C95) and of the low-first one (O100 H110 L99 C105) that
tests/test_e6_entry_bar_mask_declaration.cpp uses -- under four
declarations (nothing; OnPath then AfterPath; AfterPath then OnPath;
another incarnation), an owner whose magnitudes skip the masked end
exactly as the witness's owner does, and every refusal (before the run,
from on_bar, from the excursion hook, after the run, a NULL bar, tag 2, a
NULL handle). Its eight lots cover the witness's seven rows number for
number: lot A 12/10, 0/10, 0/0, 12/10; lot B 10/12, 0/0, 10/0, 10/12.

Fail-before against this branch's header at the E6 tip, verbatim:

    tests/test_native_c_api.c:2170:19: error: call to undeclared function
    'strategy_native_declare_opened_lot_entry_bar_mask_v1'; ISO C99 and
    later do not support implicit function declarations

After: `test_native_c_api: ok`. Two mutations of a scratch copy of the
host: a spelling that answers OK without reaching the seam fails 12 rows
(both declaring cases); one that drops the frame rule fails 36 (all four).

Found, not changed (kernel scope): the seam derives the leg order from the
open-proximity rule even when the run forces NativeRunSpec::path_order --
the thread-local override is installed only while the native driver
samples a path, never around a callback -- so the C doc states the rule
the seam applies rather than the forced order.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit a3a75cd8f270e2d4b5a93f2b37c130fd800ab617)
…5 lane E11, item 2)

README's C ABI table gains exactly one row,
strategy_native_declare_opened_lot_entry_bar_mask_v1; nothing else in the
README moves.

docs/pages/native-engine.md, minimal:
  - "Driving the kernel from C" counts 33 additive PF_API symbols, not 32
    (scripts/check_c_abi_runtime.py pins the same 33).
  - The excursion paragraph of the callback-table section gains one
    paragraph: the owner of the excursions declares where each lot's
    opening fill sat, from on_applied alone, with the typed fill point;
    the kernel derives the two flags on_lot_excursion's facts carry back;
    being a member of the base, the seam sits outside the COVERAGE census
    and the block records it in prose; the C scenario reproduces the C++
    witness number for number.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit b1d65c05a767a2f0b02de411fe8a312a64751433)
…es (R5 follow-up C-surface lane E7, item 1)

`pf_native_subscription_v1::lookahead` and `::gaps` were the one pair of
enum-valued words in <pineforge/native_c_api.h> with no `pf_native_*_e`: two
untyped `uint32_t` 0/1 words documented in barmerge terms (lane L14-C,
finding 2). They now hold a `pf_native_lookahead_e` and a `pf_native_gaps_e`
value, named after the two delivery rules `NativeTimeframeSubscription`
declares in native_run_spec.hpp:

  PF_NATIVE_LOOKAHEAD_AT_COMPLETION  = 0  the bucket rides on the input that
                                          completes it (the default)
  PF_NATIVE_LOOKAHEAD_AT_FIRST_INPUT = 1  its final values ride on its first
                                          contributing input
  PF_NATIVE_GAPS_HOLD                = 0  the last delivered bucket stands
                                          until the next delivery (the default)
  PF_NATIVE_GAPS_CLEAR               = 1  the series answers PF_NATIVE_ABSENT on
                                          an input that delivers none of it

"Completion", not "last input": a LazyComplete bucket is closed by the next
period's first input, which is not one of its own contributing bars
(NativeTimeframeBarContext, native_host.hpp).

The fields stay `uint32_t`, documented `#pf_native_lookahead_e.` /
`#pf_native_gaps_e.` exactly as every other uint32_t enum word in the header
is, so nothing moves: sizeof 32, lookahead at 4, gaps at 28 -- a C probe
prints the same nine layout numbers against this header and against
0a47cbf's. No epoch: PF_NATIVE_API_VERSION stays 1, the enumerations are
additive and their values are the 0/1 the runtime already accepted. The
COVERAGE row of declare_timeframe_subscriptions names the two words;
check_native_c_api_surface (55 members, 47 spelled, 8 excluded) and
check_c_abi_runtime (no symbol added; neither script counts types) pass
unchanged.

Ruling: generic. The names are the kernel's delivery rules, not a source
language's; the header's three barmerge mentions are gone, and the docs
pages keep the Pine mapping.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit bb6f3e410d751d0aeffce4adc5ac36a9d432ae58)
…+ bools (R5 follow-up C-surface lane E7, item 2)

translate_subscriptions read `lookahead` and `gaps` with `> 1u` range checks
and `!= 0u` conversions: the one enum-valued pair in native_c_host.cpp that
was not read by an exhaustive switch whose default refuses. Each word is now
switched onto its enumeration with `default: return PF_NATIVE_E_TAG`, the
refusal every other tag uses, at the same point of the row check (after the
NULL `tf`, before the authoritative-bar arguments). Every row is therefore
accepted or refused exactly as before, with the same status, on both entry
points that share this translation (strategy_configure_native_ext_v1's
subscription block and strategy_native_declare_subscriptions_v1).

static_asserts pin the C++ side: both rules are `bool` in
NativeTimeframeSubscription (a rule that grows a third value fails the build
before it can reach a C host with no name), the enumerators are false and true
in that order (a zero-filled row is the C++ default), both words stay
`uint32_t`, and the row's layout is pinned member by member (lookahead 4,
gaps 28, size 32 on LP64, pointer-width generic) so the typing can never move
it.

No kernel, adapter or epoch change: the kernel receives the same two bools
for every accepted row.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 7a3323bc810c62a47798fa830be6403873e43ffa)
…ast each (R5 follow-up C-surface lane E7, item 3)

check_subscription_delivery_words() in tests/test_native_c_api.c declares
four rows of one "15" series over thirteen whole buckets of the twin's "5"
feed, one row per pair of values, through strategy_configure_native_ext_v1,
and reads each rule back from C alone:

  - WHEN: every bucket of an AT_COMPLETION row rides on its third input, every
    bucket of an AT_FIRST_INPUT row on its first (on_timeframe_bar's
    delivered_at_ms), each carrying the whole bucket's OHLCV -- for
    AT_FIRST_INPUT that is its final values two inputs early;
  - WHAT: strategy_native_series_bar_v1 answers on every input once a HOLD
    row has delivered, and only on the delivering input of a CLEAR row;
  - 13 deliveries per row, 39 calculations.

One past the end of each enumeration is PF_NATIVE_E_TAG on both entry points
that take a row (configure and declare), leaving the handle Unconfigured,
and the same handle then configures and runs. The layout the typing must not
move is pinned from C too: each word one uint32_t, zero the default of both,
and on LP64 the offsets 4/8/16/24/28 and sizeof 32 measured on 0a47cbf.

Fail-before: against 0a47cbf's header the TU does not compile, for exactly
the missing spelling -- 15 diagnostics, all four names, the first
  tests/test_native_c_api.c:3299:5: error: use of undeclared identifier
  'PF_NATIVE_LOOKAHEAD_AT_COMPLETION'
The runtime rows test preserved behaviour: with the four names shimmed to
0/1 the same TU passes against 0a47cbf's library, and with either word's
two values swapped it fails (13/13 buckets off their input, 24-26 inputs
answering against the gaps word), so the rows tell the values apart.

No new CTest row: the checks run inside test_native_c_api, so neither
KERNEL_MIN_TESTS nor RELEASE_MIN_TESTS moves.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit d2225d8f7a4026945e6503746034af9411d333b7)
…them (R5 follow-up C-surface lane E7, item 4)

native-engine.md's C-surface paragraph said a row carries `lookahead` and
`gaps` "as 0/1 words" and that any value but 0 or 1 is PF_NATIVE_E_TAG; it
now names the two enumerations and their values, keeps the barmerge mapping
in parentheses, and says a value outside either enumeration is the tag
error. The `_series_bar_v1` sentence names PF_NATIVE_GAPS_CLEAR instead of
`gaps = 1`. pine-to-native.md's request.security row gains the C spelling of
the two delivery rules. Minimal row text only: both pages are being
rewritten by the docs lanes, and the integrator carries these edits.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit cd3327be6a74400718deb4f7ad6e0b9870ce443c)
…d (R5 follow-up lane E5, item 1)

Lane P9 left open whether a strategy.exit trail WITH a trailing offset arms
(activates) on the raw price path or on the tick-quantized one, and noted that
no tape pinned it. These are the tapes that do: nine `lab tv` exports
(pineforge-workflow, channel ws-report-v1, rangeProof covered), each directory
holding strategy.pine, tv_trades.csv, meta.json and metrics.json unmodified.

- NYSE:F 15m, 2025-04-10 .. 2026-04-17: three shorts and three longs whose
  exit bar has a raw extreme inside the activation's tick cell (lows 9.415 /
  13.041 / 12.641, highs 11.899 / 13.049 / 13.419), with trail_offset 1
  (e5-f-short-arm-qlow, e5-f-long-arm-qhigh) and the one-shot controls with
  trail_offset 0 (e5-f-*-oneshot-*).
- BINANCE:ETHUSDT.P 15m, 2025-04-20 .. 2026-04-25: 8 longs and 8 shorts whose
  later bar touches the entry fill exactly, trail_price = fill +/- 0.004 or
  0.006 and trail_points 0.4, each with trail_offset 1.

bars.inc carries, per entry, the feed bars the probe replays (the NYSE:F
registry feed 80f404ae..., the corpus 442d497 derived ETH 15m feed 27b62431...);
README.md records the provenance. No test reads them yet.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit d80e411d3e9086478fa3c05f17a6c6594a60b0ac)
… (R5 follow-up lane E5, item 2)

TradingView's tapes (item 1) answer lane P9's open question against the
product. On NYSE:F, with trail_offset 1, all six trades exit on the bar whose
raw extreme stops inside the activation's tick cell and only its quantized
value reaches the activation (9.415 -> 9.41, 13.041 -> 13.04, 12.641 -> 12.64,
11.899 -> 11.90, 13.049 -> 13.05, 13.419 -> 13.42), each at activation -/+ one
tick. The adapter armed the generic Trail at the raw activation and exited one
to four bars later, or at the timeout. The one-shot controls exit on the same
bars at the activation; the ETH tapes show a sub-tick level is never rounded
onto the fill (0 of 32 arm on the touching bar).

Ruling: a trail's activation is quantized per order kind (the arm on the
quantized path, the running best and the trail stop raw), which is design B2's
shape and adapter policy. ADR-0001 "Trail and tick conventions" already names
the half-tick arm as the adapter's, and the kernel's generic rule for a trail
arm under a declared grid exists since L8b; selecting it for the arm alone is
the per-order-kind mask native-feature-parity.md section 3.6.2 rejected. So
the kernel is untouched (no header, no epoch, no C surface) and the adapter
spells the rule:

- source_trail_arm_level: the half-tick boundary where the quantized path
  first reaches the activation, the threshold the one-shot leg rests at
  (source_trigger_threshold). exit() arms a trailing leg there, and so does
  the anchored-leg hook resolve_anchored_level, so an adopted relative child
  is still bit-equal to the fill point's request.
- The re-issue identity check compares the attempted arm with the prior
  source level's lowering instead of the level itself.
- The booked price reads a running best that is never short of the
  activation: TradingView's starts there (13.041 books 13.05, 11.899 books
  11.89), while the kernel's starts at the arm.

Witness tests/test_offset_trail_quantized_arm.cpp replays every tape trade and
reads the kernel's record (the Trail's arm is the activation's half-tick
boundary, the TrailArm activation and the close land on TradingView's exit
bar). Against the unchanged adapter: 541 checks, 48 failures (6 NYSE:F exits
late, the arm is the raw activation). With this change: 0 failures.

expectation corrected (test_trail_activation_tick_bar{,_l4c}): the P9 row
asserting the hold of a 9.996 high under a 10.00 activation now asserts the
fill @9.98, with the kernel's own stop at 9.976; its twin-parity rewrite
evidence is re-pinned (twinAssertions 41 -> 45, new digest) and so is
INVENTORY_SHA256 in scripts/check_twin_parity.py, outside the lane's file list
by necessity. Residual, same kind as B2: while the raw extreme stays inside
the activation's tick cell the kernel's stop sits up to half a tick further
out than TradingView's. Corpus 442d497 byte-identical 312/312 (on-grid, both
arms admit the same prints); the population sweep over sub-tick feeds is the
real gate.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 3bd6a36b13f08c9d1d36e6602cdb0ea053e2874b)
…ttled by TradingView's tapes (R5 follow-up lane E5, item 3)

native-feature-parity.md: the B2 row names the trailing leg's activation among
the quantized kinds, and a paragraph under the section 3.6.2 table closes lane
P9's open question: the evidence, why it stays adapter policy (ADR-0001, the
rejected per-kind mask), what the adapter now does, corpus byte-identity with
the population sweep as the real gate, and the half-tick residual of the
running best.

native-engine.md: the grid section's per-kind sentence covers the trailing
leg, and the P9 section records the settled rule and the corrected row.

pine-to-native.md: the trail row states the quantized activation, the
adapter's half-tick arm and the kernel's raw compare.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit b796227d579a99c44c67bb45a680e1753b7ade28)
…, byte for byte as exported (R5 follow-up lane E9, item 1)

Lane E5 left two questions open (exec/E5-final.md "(f) Findings"): the price a
one-shot trail (trail_offset 0) books when its trail_price is sub-tick, and
whether a placement close inside the activation's tick cell counts as already
reached. These are the tapes that answer them: six `lab tv` exports
(pineforge-workflow, channel ws-report-v1, rangeProof covered), each directory
holding strategy.pine, tv_trades.csv, meta.json and metrics.json unmodified.

- BINANCE:ETHUSDT.P 15m, 2025-04-20 .. 2026-04-25: e5-eth-long-oneshot-p004
  (lane E5's one-shot control, trail_price = fill + 0.004: 5 of 8 exit at the
  tick past the level, fill 2550.85 -> 2550.86) and its short twin
  e9-eth-short-oneshot-m004 (fill - 0.004: 4 of 8 at fill - 0.01).
- NYSE:F 15m, 2025-04-10 .. 2026-04-17: seven longs and one short whose
  placement close is half a tick short of the activation and quantizes onto
  it (11.295 -> 11.30 ... ; 14.415 -> 14.41), with trail_offset 1 and 0
  (identical tapes): every trade exits on the next bar, at its open.

bars.inc carries, per NYSE:F entry, the registry feed bars 80f404ae... the
probes ran on; the ETH tapes reuse tests/fixtures/offset_trail_arm/bars.inc.
README.md records the provenance and TradingView's refusal of a trail without
trail_offset. No test reads them yet.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 52961c8629a2ab3e78a3dd40572f8eb8518e7b30)
… away from the position (R5 follow-up lane E9, item 2)

Lane E5's one-shot control (strategy.exit trail_offset 0, trail_price = fill
+ 0.004) differed from TradingView on 5 of 8 trades, and its short twin
(fill - 0.004) on 4 of 8, identically before and after E5. The adapter lowers
the one-shot to a generic Limit resting at the half-tick boundary where the
tick-quantized path first reaches the level (one_shot_trail_trigger ->
source_trigger_threshold: 2550.854 rests at 2550.855), then booked the
crossing with the stop-style snap TOWARD the position,
directional_tick(activation, is_buy) = 2550.85, in both zero-offset settlement
sites of resolve_terms: the unarmed reach of zero_trail_policy_price's path
walk and the explicit-zero block that overrides every one-shot filled inside
a path. That price lies on the wrong side of the resting limit, so the
kernel's generic limit-or-better check (NativeExecutionConsumer::
consume_matched_request, native_execution_consumer.cpp:4426-4443: a
non-fill-through sell limit refuses resolved < level) returned
MatchRejectedEvent InvalidTerms, terminal; the exit never happened and the
trade closed at the timeout. TradingView books 2550.86 and 1791.82.

Ruling (adapter): the kernel is right and untouched. A booked price the
resting limit forbids is legitimately refused; the defect is the adapter's
price. A one-shot trail's activation is reached on the quantized path, so the
price it books is the tick that path first reaches there: the resting level
rounded AWAY from the position (source_one_shot_reach_tick = directional_tick
(resting level, !is_buy), the grid point the half-tick threshold stands for),
which the limit admits by construction. On the grid it is the activation
itself (k * tick either way), so on-grid outcomes are byte-identical; a
generic Trail (a zero-offset trail already reached at placement) keeps the
old snap. No header, no epoch, no C surface.

Witness tests/test_trail_activation_tick_reach.cpp replays both tapes
(tests/fixtures/trail_activation_tick_reach) and reads the kernel's record:
the Limit's level is the outermost price whose tick is the reach tick, no
match is refused, every TradingView trail exit is filled at that tick on
TradingView's bar. Against the unchanged adapter: 235 checks, 36 failures
(long 5 of 8 and short 4 of 8 exits late at the timeout, each refused
InvalidTerms offered @2550.85 / 1791.83 against 2550.855 / 1791.825). With
this change: 262 checks, 0 failures. The full-feed harness (pinned codegen
89645d6 + scripts/run_strategy.py on the corpus ETH feed) goes 3/8 -> 8/8
and 4/8 -> 8/8.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 85f19a09c6ce0a5b26461765f4d46a7993411e7b)
…arried best starts there (R5 follow-up lane E9, item 3)

Lane E5 left one question unpinned: does a placement close that sits inside
the activation's tick cell count as already reached? The tapes of item 1 answer
it. On NYSE:F 15m, seven longs and one short issue strategy.exit(trail_points
n) at the close of the bar whose open filled the entry; that close is half a
tick short of the activation and quantizes onto it (11.295 -> 11.30, 11.575 ->
11.58, 13.055 -> 13.06, 14.135 -> 14.14, 13.705 -> 13.71, 13.385 -> 13.39,
11.565 -> 11.57; short 14.415 -> 14.41), and the next bar never reaches the
activation's half-tick boundary on its raw path. TradingView exits all 8 on
that next bar, at its open, with trail_offset 1 and 0 alike (identical tapes);
with offset 1 at activation -/+ 1 tick, not close -/+ 1 tick. The adapter read
the placement close raw, left the trail dormant (a Trail waiting for its arm,
or a resting one-shot Limit) and exited 1 to 20 bars later: 0 of 16 trades.

Ruling (adapter): the placement print is one more print of the quantized path
the activation is tested on (E5's ruling 1 applied to the placement point),
and a trail armed that way carries a best that starts at the activation (E5's
ruling 2). source_trail_reached_at compares a print with the activation's
half-tick arm (source_trail_arm_level), and replaces the raw compare in exit()
(already_reached, which arms the trail at placement: the generic Trail is
submitted without an arm, the one-shot is not rested) and at the sibling
stop of an explicit-zero trail armed at placement, whose level becomes the
carried best max(close, activation) for a long, min for a short; resolve_terms
reads placement reach the same way (placement_reached_trail_activation,
zero_trail_source_price) and zero_trail_policy_price's carried placement best
never starts short of the activation. On an on-grid feed no print lies in the
half cell, so outcomes stay the raw compare's, save a close on the
activation's own grid point that the raw compare missed by a binary64 ULP.
The kernel is untouched: it already arms a Trail without an arm at its first
print. No header, no epoch, no C surface. The omitted-offset shape follows
the same compare; TradingView refuses it standalone at compile time, so no
tape pins it on its own.

Witness tests/test_trail_activation_tick_reach.cpp section 3 replays the four
placement-cell tapes and reads the kernel's record (no resting one-shot
Limit, a Trail accepted without an arm, a leg closing the trade on the next
bar). Against item 2's adapter: 530 checks, 69 failures (16 trades late, 13
at a wrong price; 8 dormant legs). With this change: 530 checks, 0 failures.
The full-feed harness (pinned codegen 89645d6 + scripts/run_strategy.py on the
registry feed) goes 0/16 -> 16/16; E5's ten probes stay 60/60.

expectation corrected (test_trail_activation_tick_bar{,_l4c}): no fill ->
fill @9.98 on this bar for the carried best 9.996 under the 10.00 activation,
because these tapes pin a placement close inside the activation's tick cell
as reached, with the best at the activation (kernel stop 9.97 from the 9.99
open, the booked price 10.00 - 0.02, as the row's carried-best-AT-10.00
control); the row is renamed test_product_carried_best_arms_on_its_tick. Its
twin-parity rewrite evidence is re-pinned (twinAssertions 45 -> 49, new
digest; the reason names the E9 tapes) and so is INVENTORY_SHA256 in
scripts/check_twin_parity.py, outside the lane's file list by necessity, as E5.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 3d9b1b944df2d666eed47f5c256dc79ac8e8301d)
luisleo526 and others added 20 commits September 22, 2026 05:56
…R5 gap lane E12, item 3)

`examples/native/native_bracket_strategy.cpp` keeps one keyed resting bid in a
`tk::OrderBook<std::string>` and drives it through every action a host can
meet — submitted fresh, replaced in place, a replacement the kernel refuses
(`Reduce{ExplicitUnits{0.0}}` → `InvalidQuantity`), then withdrawn — reading
the outcome each time and never the bare handle. It asserts the two facts the
handle cannot carry: a re-pricing issued no submit (the order the host named
kept its place), and a refused replacement issued no submit either and left
that order working and still bound to the key. It also asserts that an unknown
key is the book's own silence: `known == false`, nothing commanded.

The bid rests at 90/91 under a market around 100 and is cancelled on the last
bar, so it never fills, never joins the bracket's group and moves no trade:
the row's assertion, `closed trades: [1-9]`, is unchanged, as is every line
the example printed before ("working before the fill: 1", "working after the
fill: 2", both arms, one long trade). Its own new lines are printed after the
asserts that produced them.

docs/pages/native-engine.md gains the `OrderBookAction` table beside the
existing `BracketLegState` one and states the ruling in the same words: the
kernel's `ReplaceResult`, `SubmitResult` and `CancelResult` are the truth and
are carried verbatim, `action` is the toolkit's only added word, and the two
legacy spellings are projections — `submit_or_replace(...)` is
`submit_or_replace_outcome(...).handle()` and `cancel(key)` is
`cancel_outcome(key).status()`, exactly. docs/pages/pine-to-native.md's
cancel-by-id row gains one clause for the same, and its stale
`native_toolkit.hpp:124` anchor for `OrderBook` is corrected to :309.

No C change. The toolkit has no C spelling (it is header-only composition
over the public request surface and emits no library symbol), and the C
replace already answers a typed status: `strategy_native_replace_v1` returns
`PF_NATIVE_OK` / `PF_NATIVE_E_REJECTED` / `PF_NATIVE_E_NOT_WORKING` /
`PF_NATIVE_E_INVALID_TARGET` / `PF_NATIVE_E_STATE`, which is `ReplaceStatus`
plus the command-legality code, with the successor handle written out.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 129c0be192b6bb7f8a77acb80dc99a61f076232c)
…ollow-up lane E13, item 1)

`pf_native_request_v1` has always said whether a TRAIL carries an arm price
(`trail_has_arm_price`), but the working-book readout `pf_native_working_v1`
carried only `p1`/`p2`: `working_trigger_tag()` (called by `fill_working()`)
wrote `p2 = *arm_price` when the optional held a value and left it 0
otherwise, so a trail with no arm and a trail armed at 0.0 came back as the
same row. Since lane E1 an anchored trail's absent arm is a legal spelling
of its own, which made that observable: the two waiting legs of the new C
scenario agree on intent, owner, trigger, trigger state (every Trail starts
at TrailWaitArm), remaining kind, p1 and p2 -- nothing but their own labels
told them apart.

The readout now carries the request's own flag, filled from the live
definition's `std::optional<double>`. Before the arm an anchored trail reads
back the spelling it was submitted with; the arm installs the level into the
definition, and both spellings then read 1 with that level in `p2`. A
tick-spelled trail offset needs no mirror: acceptance resolves it into
`Trail::offset` in place, so `p1` is already a price distance.

Ruling -- growth mechanism, no epoch. The header's hardening rules make
`struct_size` exact EXCEPT for a deliberately additive tail, and that path
has been taken six times at `PF_NATIVE_API_VERSION` 1 (the request's L7b and
L3b tails, the callback table's hook tail, the spec extension's L9, N8 and
auxiliary tails): append after the last field, publish the earlier length as
the offset of the first appended field, accept both lengths. This is the
first READOUT to take it, and a readout adds one obligation an input does
not have: the runtime writes, so it must never write past the length its
caller sent. `strategy_native_working_get_v1` now accepts
`PF_NATIVE_WORKING_V1_BASE_SIZE` or the current sizeof (anything else stays
PF_NATIVE_E_STRUCT), fills a full local row and copies exactly the caller's
length back. A static_assert proves the base length IS a base-layout
caller's sizeof on the runtime's target (the tail starts where `comment`
ended and that end carried no padding), and one pins the tail to its two
words. The alternative -- reusing the row's `reserved0` word, as
`pf_native_subscription_v1::gaps` did -- is refused for a readout: an older
runtime writes 0 there, so a newer caller would silently read "no arm"; with
the appended tail an older runtime refuses the newer length instead. The
STABILITY paragraph's "raises the version constant" describes a revision
that refuses earlier callers; an additive tail keeps them, which is why none
of the six earlier tails bumped it and this one does not either.

Generic capability, not a Pine one: the C transport reads back a C++
optional it already had. No kernel or adapter source is touched, no symbol
is added (check_c_abi_runtime.py unchanged), and the COVERAGE row for
`native_working_requests` names the field. The HARDENING RULES bullet named
one additive struct and three layouts; it now names the four structs that
carry tails and the readout's write bound.

Test first. Against the unchanged header the new scenario does not compile
(first diagnostic: "no member named 'trail_has_arm_price' in 'struct
pf_native_working_v1'", 15 errors). With the header grown and the runtime
unchanged it runs and fails for the right reasons: "the 0.0-arm trail reads
back no arm price", "a base-length working row was refused", "an armed trail
reads back no arm price". After the runtime change it passes, including a
base-length read whose bytes past PF_NATIVE_WORKING_V1_BASE_SIZE keep the
caller's 0xA5 sentinel and a third length refused.

No ctest row is added (the scenario runs inside test_native_c_api), so
KERNEL_MIN_TESTS and RELEASE_MIN_TESTS do not move.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 5cc28340c4e2e363494973ac61ebd991c0455bcd)
…5 follow-up lane E13, item 2)

README's C ABI table row for `strategy_native_position_v1` /
`_working_len_v1` / `_working_get_v1` names the readout's appended
`trail_has_arm_price` and the base length an earlier caller sends.
native-engine.md gets one paragraph beside the request's published lengths:
`pf_native_working_v1` is the first readout with an additive tail, what the
flag reads before and after an anchored trail's arm, and the obligation a
readout has that an input does not -- a caller sending
`PF_NATIVE_WORKING_V1_BASE_SIZE` is written exactly that far and never past
it.

No ADR-0001 row: the ADR has no C-surface growth table. Its ruled tables
cover TradingView-named residue in the kernel archive (a struct field is not
a symbol or string there), deprecated public spellings (nothing is renamed)
and `NativeRunSpec` fields the adapter does not declare (this is a readout
field, not a spec field), and the header's own growth rule asks for none.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 2f27bd194073aaf488eb2d523997821a663b48eb)
…landed (R5 follow-up lane E14, item 1)

A trailing stop rides a running best, and until now the kernel had no way to
say where that ride begins. The best was the arm's own print: the arm
threshold's ladder point when a crossing armed the trail, the first print the
trail saw when it was submitted already armed. Both are accidents of WHEN the
arm happened rather than a decision the run made, and a host whose position
already reached a level before the request existed could not say so — it had
to restart the ride at a worse price and watch its stop sit too low.

native_order::Trail::best_seed is that level, appended last so every existing
{offset}, {offset, arm_price} and {offset, arm_price, ticks} initializer keeps
its meaning. Present, it is a floor on the start: at the arm the best becomes
the favourable one of the seed and the arm print, the higher for a sell trail
and the lower for a buy trail. Absent — every request in the tree today — the
arm is byte-for-byte what it was, which the two control rows pin by their
continuation digests, measured on this lane's base before the field existed
(7148617029230866814 and 1059492877796320724).

The ruling, under ADR-0001 rule 2. Mechanism: a trail that rides from its
activation instead of from the next print is a broker shape a second venue
asks for, and the level is data the host names — the kernel neither derives it
nor knows what named it, exactly as it does not know what named an arm_price.
Knob: today's start is not a choice the run made another way; there is no
second spelling of it, and keeping the arm threshold as the start is not "the
run already decided" but "nobody decided". So a request field is admissible
rather than a hook. The seed is absolute — an anchor moves the arm threshold,
never the level the ride starts at — it must be finite and positive, and under
a price grid it is put on the ladder exactly like an observed print, so L8b's
invariant that the running best lives on the ladder still holds. The
activation event still reports the print the arm happened at, never the seed,
which the existing reached_price pins require.

The digest folds the seed only when present, under its own "best_seed" tag so
it can never read as an arm price. No epoch: the field is appended inside
native_order_v6 the way WaitForApplied::first_match and FromOwnerFill::rounding
were, and NativeTrailState — the projection the C++ ABI matrix pins — is
untouched.

tests/test_native_trail_best_seed.cpp is the witness, kernel-only, batch and
stream: a sell trail already armed at submission whose next print is 100.60
while the host names 101.00, and a sell trail armed on a crossing of a
half-tick threshold whose bar high stops at 100.999 while the host names
101.00. In both the later shallow bar reaches between the two stops. Plus the
buy mirror, an off-ladder seed under QuantizeFillsAndTriggers, and the
acceptance rows.

Fail-before, compiled against this lane's base 9f0ea24c:

    tests/test_native_trail_best_seed.cpp:80:19: error: no member named
    'best_seed' in 'pineforge::native_order::Trail'

and the same two bar sequences driven through the unchanged kernel
(exec/E14-probes/fail_before.cpp) book no exit at all:

    A control  rows=0 exit=0 position=1 continuation=7148617029230866814
    B control  rows=0 exit=0 position=1 continuation=1059492877796320724

After:

    PASS trail best seed: 155 checks, 0 failures

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit c2a48fc2b55286c224386e981d1b35325f0f0c33)
…uest layout (R5 follow-up lane E14, item 2)

pf_native_request_v1 grows one tail: `trail_best_seed` and its
`trail_has_best_seed` flag, the C spelling of native_order::Trail::best_seed.
That makes four published layouts, and the runtime accepts every one of them:
the base the L13 lane shipped, plus L7b's anchored-leg tail, plus L3b's
sizing detail — which now has a name of its own,
PF_NATIVE_REQUEST_V1_SIZING_SIZE, defined as the offset of the first field
appended after it so it stays right on every target — plus this one. A caller
compiled before the seed sends the third length and its trail rides from the
arm print exactly as it did; the tail is not even read.

The hardening rules are met where they apply: the flag is range-checked
(anything but 0 or 1 is PF_NATIVE_E_TAG) and only when the caller carries the
tail, so an older layout is never judged on bytes it does not have; the level
itself is the kernel's own validity rule, answered as PF_NATIVE_E_REJECTED.
Three static_asserts pin that no tail moved.

The COVERAGE list is unchanged — the seed is a request field, not a
NativeStrategyHost member — but the header records the asymmetry a C host
will look for: pf_native_working_v1 keeps two trigger numbers and a trail now
has three. The seed is a submission input consumed once at the arm, and what
a host wants back is the ride, which strategy_native_trail_state_v1 already
answers as best_price and current_level; growing the working POD would cost
every compiled reader a layout for a number the caller wrote.

tests/test_native_c_api.c gains check_trail_best_seed_tail: the same five
bars as the C++ witness, run three ways. Unseeded the lot is still open at the
end; seeded it closes; and sent with PF_NATIVE_REQUEST_V1_SIZING_SIZE while
the seed bytes sit in the struct, it closes nothing — which is the whole
append-only claim. Plus the out-of-range flag and the nonpositive level.

    test_native_c_api: ok
    test_native_c_api_frozen_header: ok
    check_native_c_api_surface: 55 NativeStrategyHost members, 47 with a C
    spelling, 8 excluded with a reason
    check_c_abi_runtime: exit 0

docs/pages/native-engine.md's request paragraph counts four lengths now, and
names the seed among the trigger's numbers.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 84847ee935b20b4a9a2af9e63b0300b855593d4d)
… lanes' residual (R5 follow-up lane E14, item 3)

Lanes E5 and E9 each left the same residual, and it is one sentence:
TradingView's running best starts AT the activation, the kernel's started
wherever the arm happened to land. Two shapes, both measured:

  - armed on a crossing (E5 ruling 4): the kernel's best is the half-tick
    threshold the adapter arms at, so its stop sits half a tick further out
    than TradingView's. None of E5's six NYSE:F tapes hit the gap.
  - armed at placement (E9 finding 2): the arm price is dropped, so the
    kernel's best is the NEXT bar's first print. All eight of E9's tapes fell
    far enough to hide it; a shallower next bar would diverge.

With item 1's generic Trail::best_seed the leg simply says where the ride
starts. exit() names the source activation, and when the placement print has
already reached it, the favourable one of the two — the same carried best
max(close, activation) for a long, min for a short, that E9's sibling stop
already rests at. The kernel floors its running best there at the arm; nothing
else about the leg changes, and no other adapter site does.

That is not a new TradingView rule in the kernel: the kernel is told a number
and floors a max, exactly as it is told an arm price. Which number is
TradingView's is decided here, where E5's ruling 2 was measured.

Three twin suites move, and every one of them moves the kernel's own facts
TOWARD the price the adapter already booked. No booked exit price, bar or
quantity changes in any row:

  expectation corrected (test_trail_activation_tick_bar{,_l4c}): raw_price
  9.976 -> 9.98 and path_position 2 + 0.02/0.021 -> 2 + 0.016/0.021 for the
  offset-trail arm row, and raw_price 9.97 -> 9.98 for both carried-best rows,
  because the kernel's best no longer starts at the arm (9.996 raw) or at the
  next open (9.99) but at the activation 10.00 the leg names, so its own
  crossing IS the booked 10.00 - 0.02.

  expectation corrected (test_trail_fill_snap{,_l4c}): raw_price 9.855 -> 9.85
  and 9.97 -> 9.975, because the seeded best is the carried level again — the
  very level the deleted exit-path resolver read — instead of the first live
  print; the booked 9.85 and 9.97 are unchanged.

  expectation corrected (test_trail_open_arm_subtick_offset{,_l4c}):
  leg_is_stop -> leg_is_trail for the already-armed zero-distance rows,
  because the gapping open 1475.99 is now strictly past the Trail's own
  seeded best 1501.03, so the Trail books the print its sibling stop used to.
  Price, bar, at_bar_open and level_fill are unchanged.

Their observable-rewrite evidence is re-pinned (three twinAssertionSha256,
reasons extended; the assertion counts are unchanged) and so is
INVENTORY_SHA256 in scripts/check_twin_parity.py, outside the lane's file list
by necessity, as E5 and E9 both were.

The sibling stop STAYS, and it is not redundant. A kernel Stop is reached by a
touch; a zero-distance trail needs a move strictly past its best
(trail_zero_stop_hit), so the two differ exactly when a print lands ON the
carried best. Measured with the stop withheld, on a long carrying best 100.10
into a bar O 100.10 H 100.12 L 100.05:

    with the sibling stop:     exit 100.09999999999999 raw 100.09999999999999 at_open=1 leg=Stop
    with it withheld:          exit 100.10000000000001 raw 100.11500000000001 at_open=0 leg=Trail

The booked price differs in its last bits, so removing it would not be
byte-identical. (exec/E14-probes/sibling_stop_touch.cpp.)

    check_twin_parity: 155 tests, 8298 base assertions, 6657 literal-matched
    twin assertions, 1262 ledgered, 379 pinned observable rewrites, 528
    additional twin assertions, OK
    corpus_trades_identity: 312 probes at corpus 442d497 — baseline match=312,
    moved=0, unrecorded=0, missing=0, schema violations=0
    Verified 312 strategies — excellent=311, strong=0, moderate=0, weak=0,
    minimal=0, anomaly=1, engine_only=0, missing=0
    [corpus-parity] TradingView parity holds at corpus 442d497

The corpus cannot exercise the seed's own effect: its feed is on the 0.01
grid, so the arming bar's own extreme is folded into the best before any
adverse move and the seed changes nothing there. The wave-D sweep over
sub-tick feeds is the real gate.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 4f5cdf3e1f0e84b7bf2e1edd55b6d9189606edcf)
…et the kernel learn it (R5 follow-up lane E14, item 4)

Four pages, one fact each.

native-engine.md gains `Trail::best_seed` beside the other trail spellings —
what absent means (the arm's own print, byte for byte as before), what present
means (a floor on the start, the favourable one of the seed and that print),
that it is absolute, finite and positive and goes on the ladder like an
observed print, that the activation still reports the print the arm happened
at, and that the digest folds it only when set. Its trail section then records
what lane E14 closed: the residual E5 and E9 each left, why the sibling stop
of an explicit-zero trail is NOT redundant afterwards, and that the kernel's
own crossing is now the price the adapter books.

pine-to-native.md's trail row carries the seed in the kernel spelling and one
sentence on who names it.

native-feature-parity.md §3.6.2: the B2 row says which part of it was
closable and is closed — where the running best starts is not a quantization
but a level the leg names — E5's residual paragraph is past tense, E9's names
the kernel half, and a new paragraph records lane E14 against the amended
rule 2 with the corpus and sweep caveats.

ADR-0001 gains one section and one row. Rule 2's hard case is the capability
whose NEED was found by measuring TradingView while the mechanism is a shape
any venue has, and the amended rule asks two things: is it drivable by data,
a spec field or a hook, and would spelling it put a hash-visible choice on the
surface for a decision the run already made. `Trail::best_seed` answers
generic and admissible — a trail that rides from its activation is an ordinary
broker shape, the level is a number the host hands over as it hands over an
arm price, and the old start was nobody's decision but wherever the arm
landed. The section is deliberately its own `##`, outside the residual
vocabulary section, so `check_kernel_residuals.py` cannot read its table as a
stale ruling.

    check_kernel_residuals: 109 archive hits (106 identifiers, 3 texts)
    against 119 ruled identifiers and 11 ruled texts: 0 findings ... OK
    check_native_feature_rulings: 43 NativeRunSpec fields, 33 declared by the
    adapter's project(), 10 ruled, exit 0

Merge note: lane L14-C (origin/r5/int12-docs) rewrites ADR-0001 and adds its
own "TradingView-calibrated kernel mechanisms (rule 2's own account)" section.
On a merge, this row belongs in that section's table B and this section goes
away; the wording here is already the amended rule's.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit bca4c902e2755a501ce9af6d337eee28484cebb3)
…nning best told apart (R5 follow-up lane E14, item 5)

Lanes E5 and E9 recorded the same residual and neither could hit it: all
fourteen of their tapes had a next bar that fell far enough for either reading
of the running best to exit. These two do not.

`rank_shallow.py` searched the NYSE:F 15m registry feed (evidence
80f404ae…, `lab evidence get`) for an entry whose placement close reaches the
activation on its tick AND whose next bar is SHALLOW: its own extreme half a
tick to a tick short of the carried best, its other extreme BETWEEN the two
stops, carried - k ticks and restart - k ticks. Eleven such entries, seven
long and four short, `trail_points` n with `trail_offset` 5, exported with
`lab tv` (ws-report-v1, both `rangeProof: covered`) and committed under
tests/fixtures/trail_activation_tick_reach/ beside lane E9's six.

TradingView exits all eleven on the next bar, at the carried stop's reach
tick. Its running best is the carried level and never the leg's first live
print. The four shorts are what pins that level RAW rather than quantized:
their placement closes sit half a tick PAST the activation (13.035 under
13.04, 13.925, 14.075, 13.295), TradingView's stop is that close + 5 ticks,
and the next bar's high reaches it where the activation's own stop never
would. They also pin the booked price's direction: a trail stop is reached
from the ADVERSE side, so a short's books the grid point at or above it
(13.085 -> 13.09) — the other way from the one-shot limit lane E9 pinned.

Through the product (pinned codegen 89645d6 -> strategy.dylib ->
scripts/run_strategy.py on the registry feed), with the adapter seed withheld
and with it:

    e14-f-long-shallow-next   0/7 -> 6/7
    e14-f-short-shallow-next  0/4 -> 4/4

The seventh is a RECORDED divergence, and it is not this question.
2025-09-10's stop is 11.44 - 5 ticks, whose binary64 value
11.389999999999998792 lies one ULP UNDER the bar's low 11.390000000000000568;
the trail stop is raw by lane E5's ruling, so it is not reached and the trade
runs to the timeout while TradingView books it. That is the tick-product
residual on the trail stop and needs its own lane. The test asserts the row
DIFFERS (`kLongShallowUlpResidual`), so it fails if the engine ever silently
starts matching and the mask is not removed.

tests/test_trail_activation_tick_reach.cpp gains section 4 over the new
probes — the shallow bar's geometry, TradingView's exit, and the accepted
leg's own `Trail::best_seed` — and its replay loop learns the recorded-
divergence mask. Section 3 is gated to the placement-cell tapes, whose
question the new ones do not ask.

    PASS trail activation tick reach: 717 checks, 0 failures
    PASS offset trail quantized arm: 541 checks, 0 failures
    trail_activation_tick_bar: 66 passed, 0 failed
    test_trail_fill_snap_l4c: 249 passed, 0 failed
    test_trail_open_arm_subtick_offset_l4c: 215 passed, 0 failed
    test_zero_offset_trail_rides_l4c: passed=450 failed=0
    check_twin_parity: 155 tests, 8298 base assertions, 6657 literal-matched
    twin assertions, 1262 ledgered, 379 pinned observable rewrites, 528
    additional twin assertions, OK

The adapter comment now records why the carried best is read raw. No logic
moved from item 3.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 0ddc03548ccce202211a41d1ce7fedf2c71bc5ca)
… item 1)

BacktestEngine::declare_opened_lot_entry_bar_mask derived a lot's entry-bar
mask from internal::bar_path_uses_high_first, whose thread-local override
only NativePathOrderScope installs, and only while the consumer materializes
an intrabar sample path (native_execution_consumer.cpp). A host calls the
seam from on_native_applied, outside that scope, so under a forced
NativeRunSpec::path_order the mask was computed for the open-proximity order
the run never walked: the low-first bar O100 H110 L99 C105 with an ON_PATH
fill at 105 gave (high, low) = (0, 1) under AUTO, HIGH_FIRST and LOW_FIRST
alike, where the HIGH_FIRST walk O->H->L->C touches 105 on its first leg,
before either end, and masks nothing. Pre-existing: the adapter made the
same calls before lane E6.

The seam now asks the consumer for the run's order
(NativeExecutionConsumer::path_high_first: path_uses_high_first over the
spec's path_order, the resolution deliver_confirmed_script walks) and finds
the fill's first touch on that same walk (the two-argument
first_touch_position). AfterPath is order-free and unchanged. The engine.hpp
and native_c_api.h docs of the seam state the order it now applies.

RULING (ADR-0001 rule 2): the mask is derived from the same path order the
matcher walks. The run already made that decision through path_order, so
this is not a knob; it is the existing choice applied consistently. It is
generic: nothing here names a platform, and every host (C++, C through
E11's strategy_native_declare_opened_lot_entry_bar_mask_v1, or the Pine
adapter) gets the one answer.

Consequence: a run under a forced order gets different masks, hence
different owner excursions, than before. Codegen never declares an order
(no path_order in pineforge-codegen-oss); the Pine adapter declares one only
when its embedder calls set_path_order / strategy_set_path_order, which in
this repository is scripts/run_strategy.py --path-order alone: no
runtime_overrides key carries it, neither in run_strategy.py nor in the
campaign verifier, and run_corpus.sh never passes the flag. Under AUTO the
resolved order is the expression the old read returned in a callback (the
override is always 0 there), so every AUTO run, the whole corpus included,
is byte-identical by construction; a forced-order probe is judged by the
wave-D population sweep. No epoch: no layout, hash field or default moves.

Witness: tests/test_e6_entry_bar_mask_declaration.cpp gains forced
HIGH_FIRST / LOW_FIRST runs over both bar shapes and both fill points
(fail-before: (0,1) under HIGH_FIRST on the low-first bar, (1,0) under
LOW_FIRST on the high-first bar); tests/test_native_c_api.c the same numbers
through the C spelling, the order declared in the spec extension's
feed-policy block. No new ctest row.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit a754745a7cbfa16afa969cdabb7b541e5c5d31ff)
PathOrderScope (src/engine_run.cpp) was defined in that file's anonymous
namespace and never instantiated. `git grep PathOrderScope` over the tree
finds its own three definition lines and five comments; every other hit is
the consumer's live NativePathOrderScope, the version checker's pin of it,
or a frozen historical header inside a fixture tarball. Being file-local,
no other translation unit could reach it. The class is deleted, and
engine_run.cpp's object file is byte-identical before and after (md5
5c5995d65ea985b5356b639d804aaf06, built with the tree's own flags).

Five comments described the installation it never made: engine.hpp's
path_order_mode_ and set_path_order() docs, engine_internal.hpp's
set_path_order_override / path_order_override docs, and the override's own
comment and the file header in engine_path_resolve.cpp. They now name the
one installer the tree has, NativePathOrderScope around the intrabar
sampler, and say that a question a host asks from a callback is answered in
the run's order by the consumer (item 1). Comment-only in all three files:
each strips (comments out, blank lines dropped) to the same bytes.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 85014b43c030aee642703bdd1d518c5d2d3ca094)
check_native_c_api_surface.py censused NativeStrategyHost's public surface
only, so a BacktestEngine member a host calls or overrides from its
callbacks got no C spelling and no stated exclusion with nothing to flag it
(E6's declare_opened_lot_entry_bar_mask was the first protected one called;
lane E11 measured that a [C] row naming it fails the old census).

The census is now opt-in for the base: engine.hpp marks such a member's
declaration with a `// @host-seam` line, and native_c_api.h's COVERAGE block
gains a BASE-CLASS SEAMS list that must name exactly the marked set, each
with a C spelling that resolves against the C headers or a stated reason,
under the host rows' own rules. A marker outside class BacktestEngine, or
one that precedes anything but a member function, fails; a name in both
lists fails as listed twice.

Seeds, from reading how CCallbackHost and PineStrategyHost reach the base:
  [C]  declare_opened_lot_entry_bar_mask: E11's
       strategy_native_declare_opened_lot_entry_bar_mask_v1 (both hosts call
       it from on_applied); it replaces E11's prose paragraph.
  [--] hash_host_extension: documented for any host (override it, call the
       base first to keep its bytes), overridden by PineStrategyHost; the C
       callback table has no hash hook (SCOPE said so; the row now holds it).
  [--] hash_source_extension: its deprecated spelling, still folded.
CCallbackHost reaches no other protected BacktestEngine member.
PineStrategyHost reaches more (bar_fill_price, current_equity, open_profit,
the request.security machinery, broker_state_hash_projection, the source_*
virtuals): source-layer plumbing that ADR-0001 governs, none documented as
a host seam, so none is marked.

Self-tests: six new must-fail cases (a marked member without a row, a row
without a marker, a seam spelling naming nothing, a seam row among the host
rows, a marker outside the class, a marker on a data member). Against the
previous checker 6 of the 11 fail, the real tree included.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit f35d85966da007294e25e03f3defb3bda677dae1)
…item 4)

ADR-0001, "Kernel state the adapter sets": one new ruling row and lane E6's
row corrected.

The new row records RULING (rule 2): the entry-bar mask is derived from the
same path order the matcher walks. The run already made that decision
through NativeRunSpec::path_order, so this is not a knob; it is the
existing choice applied consistently. It states the pre-E15 mechanism (the
seam read the sampler's thread-local override, which is never installed
around a host callback, so every declared order got the open-proximity
mask) and the consequence: forced-order runs get different masks; codegen
declares no order and the adapter declares one only through its embedder's
set_path_order (scripts/run_strategy.py --path-order, never passed by
run_corpus.sh, carried by no runtime_overrides key), so the corpus is
byte-identical by construction and the wave-D population sweep judges any
forced-order probe.

E6's row claimed path_order steers the mask; it names the mechanism that
now does (NativeExecutionConsumer::path_high_first), says the claim held
only from E15 on, and names E11's C spelling.

docs/pages/native-engine.md: the excursion paragraph says the mask walks
the run's leg order and that the seam is a row of the COVERAGE block's new
BASE-CLASS SEAMS list; the "what is not exposed" paragraph names that list.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit ad4b11f94257401d1405fbfbcb8896409ceaa3ea)
…1 follow-up)

E6's header credited the leg order to bar_path_uses_high_first, which the
seam no longer calls since item 1: the order is the run's own. Comment-only,
same line count, so the test object is byte-identical (md5
1cad302f2d565f702eedc01839743241 before and after).

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 44541b73f54eb131dd6117aa3114868cf3a24fef)
…PU time since A40 rev 7 (R5 tooling lane E4, item 1)

A40 rev 6 made .github/workflows/ci.yml set
PINEFORGE_RUNTIME_BUDGET_CANDIDATE_ONLY=1 on the hosted macOS lane, so its
Release configure registered test_l4g_runtime_budget --candidate-only: one
correctness sample, no ratio. The reason was wall clock (the same tree read
12.8x and 17.2x best-of-five an hour apart on that VM). Lane Q9 (A40 rev 7)
made the gated sample the replay's process CPU time, which leaves out the time
a leg spends descheduled; Q9's open note asked for the lane back.

ci.yml now sets the variable to '0' on every lane, with the comment naming it
as the escape for a runner whose CPU accounting cannot time the replay and
asking for the measured reason before it is used. The mechanism is unchanged:
tests/CMakeLists.txt still reads the variable at configure time (comment
updated; it said the workflow sets it for macOS). docs/ci.md says which lanes
gate, what the escape is, and the line the macOS job must print.

Witness, scripts/test_ci_verify.py RuntimeBudgetLanes: every
PINEFORGE_RUNTIME_BUDGET_CANDIDATE_ONLY assignment in ci.yml is '0', and the
CMake registration still reads the variable (so the escape the comment offers
exists). Before this change:
  AssertionError: Lists differ: ["${{ runner.os == 'macOS' && '1' || '0' }}"] != []
After: both pass. On this Mac a Release configure registers the row
--candidate-only with the variable at 1 and with the relative gate at 0.
actionlint 1.7.12 is clean on the three workflows.

Not measured here: the hosted macOS runner's CPU ratio. The PR's
build (macos-26, Release) job must show test_l4g_runtime_budget printing
"runtime budget: candidate=...s ab9714b=...s ratio=...x limit=15.000x
(process cpu time; ...)" and passing, not "candidate correctness sample".

Ruling: CI configuration, a CMake comment, docs and a tooling test. No
kernel, adapter, header or corpus change; no TradingView outcome can move; no
epoch question. tests/CMakeLists.txt is outside the lane's file list: a
comment-only edit, made because the comment described the old workflow.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 7b37d91e46b9545c5b97edf20c03b3209defbee3)
…_verify refuses one where it is not (R5 tooling lane E4, item 2)

Lane Q12 gave every examples/native executable -UNDEBUG and a ci_verify
stage (examples-assert-live) that refuses a compile keeping NDEBUG, but left
out runner/CMakeLists.txt's MODULE builds of two example sources,
native_market_example and native_selected_example ("Out of scope" in Q12's
report). Those modules are what test_native_example_batch and
test_native_example_selected dlopen and drive through the C ABI, so an assert()
in either source was live in its example_* row and a no-op in its module row.

Fail-before, ci_verify's kernel configure at 0a47cbf: both modules compile
with "-O3 -DNDEBUG -std=c++17 -arch arm64 -fPIC -fvisibility=hidden
-ffp-contract=off" (no -UNDEBUG). Mutation: assert(bars_ < 0 && "...") in
on_native_bar of native_market_strategy.cpp and native_selected_strategy.cpp,
false on every bar. Result:
  example_native_market_strategy    ***Failed  (Assertion failed ... line 35)
  example_native_selected_strategy  ***Failed  (Assertion failed ... line 77)
  test_native_example_batch         Passed
  test_native_example_selected      Passed
After this change the same mutation fails all four, the module rows with
"Subprocess aborted" and "Assertion failed: (bars_ < 0 && ...), function
on_native_bar"; reverted, all four pass.

runner/CMakeLists.txt appends -UNDEBUG to both module targets, after the
build-type flags, exactly as examples/native/CMakeLists.txt does (flags.make
now ends "-ffp-contract=off -UNDEBUG").

scripts/ci_verify.py: examples-assert-live now reads every compile whose
translation unit lies under examples/native/, whatever its target, instead of
the example_* object directories, and runs in every profile that compiles one:
release and kernel (the examples) and native (the live runner builds the two
modules there). compile_argv/compile_unit/compile_target factor the entry
parsing; OBJECT_DIR replaces EXAMPLE_OBJECT_DIR. On the real kernel compile
database of 0a47cbf the stage flags 2 of 15 targets (the two modules); after
this change 0 of 15.

Tests (scripts/test_ci_verify.py): the scripted configure compiles the two
modules in the profiles that build the live runner; ExamplesAssertLive covers
the native profile, a module compiled without -UNDEBUG (kernel and native), and
fails closed in both profiles. Against the previous ci_verify.py: 7 failures,
1 error. Three pins changed, each with an "expectation corrected" note.

Ruling: CMake runner wiring, CI tooling and docs only. No kernel, adapter,
header, codegen or corpus change, so no TradingView outcome can move and no
epoch is touched. The modules stay release builds (-O3, same visibility and FP
flags); only NDEBUG is undefined.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 016838035c47358f78d479535d87703d49a89f17)
…read (R5 tooling lane E4, item 3)

The stage required build/lib/libpineforge.a to be newer than every file under
src/, include/ and CMakeLists.txt. In the kernel profile
(PINEFORGE_BUILD_SOURCE_LAYER=OFF) that archive is the kernel alone and never
compiles src/source/ or src/compat/pine/, so after any edit there a full build
rebuilt nothing and the stage failed (lane P7's report, "Two existing issues").
The same held in every profile for a header no archive compiles (e.g.
include/pineforge/native_module.hpp, which only examples include).

Reproduced on ci_verify's kernel build of 0a47cbf: touch
src/source/pine_adapter.cpp include/pineforge/native_module.hpp, full
cmake --build (rebuilt only the examples and modules, libpineforge.a mtime
unchanged), then the stage function:
  ['src/source/pine_adapter.cpp', 'include/pineforge/native_module.hpp']
With this change, same tree, same touch: [] for both archives (80 inputs, 35
units, none under src/source or compat/pine).

scripts/ci_verify.py: archive_inputs(source, build_dir, target) lists what a
target's compiles read: each translation unit compile_commands.json compiles
into it, and every header reached through #include that resolves inside the
source tree ("quoted": beside the includer, then -iquote/-I/-isystem;
<angled>: -I/-isystem, in the compiler's order). stale_archive_sources keeps
its signature and derives build dir and target from <build>/lib/lib<target>.a.
The stage now checks both archives every profile builds, libpineforge.a and
libpineforge_kernel.a (examples and the residual gate read the latter), and
fails closed without the archive's compiles in the database. Cross-check on
the real kernel build: the scan's set equals the compiler's own depfiles
(*.o.d, in-tree entries) exactly for both targets, 80/80, nothing missing
either way.

CMakeLists.txt is no longer an input: a change there that matters reaches the
archive as a changed compile, and one that does not (a comment) rebuilt
nothing and failed the stage too.

Both trees count: the build tree's generated pineforge/version.h is read by
the compiles too, so it is an input wherever the build tree lives (in-tree, as
ci_verify's and CI's are, or out of tree). Cross-check against the compiler's
own depfiles (*.o.d entries inside the two trees) on three real builds --
ci_verify's kernel build, a CLAUDE.md build-on, ci_verify's release build:
identical sets for every archive, 80/80 (kernel) and 111/111 (with the source
layer), nothing missing either way.

Tests (scripts/test_ci_verify.py StaleBinaries): a synthetic tree pins the
reading (kernel-only archive ignores src/source, an unused header and
CMakeLists.txt; a unit, a nested header and a "quoted" neighbour are stale;
a header generated into an out-of-tree build tree is an input; each archive
against its own compiles; fail-closed on no database, no compile of the
target, no archive); driver rows pin both archives in all five
profiles, a stale kernel archive failing before ctest, and a database without
the archive compiles. The scripted configure now emits the archives' compiles
in every profile and the scripted build both archives. Against the previous
ci_verify.py: 10 failures, 1 error.

Ruling: CI tooling and docs only. No kernel, adapter, header or corpus
change; no TradingView outcome can move; no epoch question.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 9d3a7cf9ad1a32dde84cfd449480db658853bbb1)
…es their providers (R5 tooling lane E4, item 4)

Four CTest rows pair the tree against historical ABI archives:
test_script_cpp_abi, test_settlement_cpp_abi,
test_aggregate_cpp_versions_runtime, test_l4g_runtime_budget. A tree
configured with PINEFORGE_REQUIRE_ABI_RECEIPTS=OFF -- every plain configure,
CLAUDE.md's included -- registers them --skip-if-receipt-missing, and until the
six ABI providers are prepared in it each exits 77. CTest counts a skipped row
as passed, so the run still prints 100% (lane Q12's report, "Recipe gap").
Reproduced at 0a47cbf on a CLAUDE.md build-on (corpus strategies off):
  4/4 Test #503: test_l4g_runtime_budget ...............***Skipped
  100% tests passed out of 4   (the four gated rows; 540 rows registered)

scripts/check_abi_receipt_skips.py, independent of ci_verify.py: reads the rows
ctest registered (--show-only=json-v1), names every receipt-gated row that
will skip (or, registered --require-receipts, fail) for a missing input
receipt, and exits 1 when there is one, printing the recipe. It also names a
tree without compile_commands.json, which test_l4g_runtime_budget reads once
its receipt exists (Q12's second gotcha: preparing the providers in a plain
configure turns that row's skip into a failure). --prepare first prepares the
six providers into the tree with the argv ci_verify.py uses: it fetches a
missing pinned commit, reuses a matching provider, refuses a mismatched one
without deleting it.

On that build-on: the check prints the four rows as "skips" (the
settlement row missing all six receipts, the runtime-budget row also
"then fails, compile database missing") and exits 1. After re-configuring
with -DCMAKE_EXPORT_COMPILE_COMMANDS=ON, --prepare built the six providers in
98 s and the check exits 0 ("every receipt they read is present: all 4 run");
a second --prepare reuses all six.

Recipe: CLAUDE.md's REQUIRED block configures with
CMAKE_EXPORT_COMPILE_COMMANDS=ON, prepares the providers before ctest and runs
the check after it (must exit 0); docs/ci.md explains the skip contract, the
script and the same recipe.

Tests: scripts/test_abi_receipt_skips.py (10) drives the script against the
real ctest over hand-written rows shaped like the gated ones -- including a
run ctest reports as "100% tests passed out of 4" in which three gated rows
skipped -- plus the preparation plan with a stubbed runner (order, fetch
before a missing object, refusal that keeps the directory).
ReceiptRecipe in scripts/test_ci_verify.py holds the recipe's six argv equal
to the ones the ci_verify driver invokes. Registered as the CTest row
test_abi_receipt_skips (ctest passed as CMAKE_CTEST_COMMAND); before the
script existed the test failed with "No module named
'check_abi_receipt_skips'".

Floors: the new row registers in every profile (a Python row, source-free), so
the integrator adds +1 to RELEASE_MIN_TESTS and +1 to KERNEL_MIN_TESTS; this
lane edits no floor.

Ruling: tooling, a CTest registration and docs. No kernel, adapter, header or
corpus change; no TradingView outcome can move; no epoch question.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
(cherry picked from commit 005c3c54f4fd67c7eeb1c296da1373b98dc705af)
…ave D)

Two edits that belong to no single lane because each is the intersection of
two.

1. `scripts/check_c_abi_runtime.py` — `EXPECTED_NATIVE_C_API` carried
   `strategy_native_cancel_where_v1` twice. It was harmless (a frozenset
   literal) and lane E11 left it alone as cosmetic when it raised the list to
   33 names; with E11's `strategy_native_declare_opened_lot_entry_bar_mask_v1`
   added the literal now has 34 entries for a 33-symbol set, which reads as a
   miscount to the next person who edits it. The duplicate is deleted; the
   set, `EXPECTED_NATIVE_C_API_DECLARATIONS` and
   `EXPECTED_NATIVE_C_API_IMPLEMENTATIONS` all say 33 and the checker exits 0.

2. `examples/native/native_open_lots_strategy.cpp` — expectation corrected.
   The documentation wave's example asserted "HostRecorded folds none of
   them: that series is the host's" for max_drawdown / max_runup /
   max_contracts_held_all. Lane E2 ruled the opposite and made it true: the
   extremes are a property of the RUN, folded at every script calculation
   under every report policy; what the policy scopes is the recorded equity
   CURVE and the `pf_equity_stats_t` figures walked out of it. The example now
   asserts exactly that split — the three numbers are equal under both
   policies, and only the curve length differs (7 recorded points vs 0) — and
   its summary line reports both. E2 named this example as the integrator's
   step in its own findings.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The pages cite the tree by line number and both doc guards are binding since
the documentation wave (PR #272). Wave D moves those lines a long way:
`include/` loses 411 net lines to lanes E6 and E10 (engine.hpp alone -400),
`native_c_api.h` gains E7's two enums, E11's symbol, E13's readout tail and
E14's request tail, `native_order.hpp` and `native_execution_consumer.cpp`
both grow, and `src/source/pine_adapter.cpp` moves under E5, E9 and E14. On
the picked tree 574 of 1553 anchors were bad; none of that is a claim
changing, only where the claim lives.

Three passes, in this order:

1. `python3 scripts/check_doc_anchors.py --fix` until it converged
   (358 anchors re-anchored, 216 left that it refuses to guess at).
2. The 216 it refuses are ambiguous only to a symbol search. They are not
   ambiguous to the diff: every one of them was correct at origin/main, which
   the wave's own gate proved (bad=0), so each was mapped through
   `git diff -U0 origin/main..HEAD` for its own file and rewritten to the line
   the wave moved it to. 159 doc lines.
3. Five anchors survived that, because they were not line drift but claims the
   wave made false. Each is corrected by reading:
   - `native-engine.md` cited `record_script_report_point` at the call site
     lane E2 moved; it now cites the definition.
   - `native-feature-parity.md` row (e) cited `apply_slippage` "in the comment
     at engine.hpp:2129" — lane E10 deleted that comment; it now cites
     `src/native_matching.hpp:240`, where the function is.
   - `native-feature-parity.md` rows (j) and 7 and ADR-0001's "current state"
     bullet said the excursion mask pair is "written only by the adapter" at
     `pine_strategy_host.cpp:471`. Since lane E6 no host writes it: the
     excursion owner declares where its fill sat and the kernel derives the
     pair. The three citations now name `declare_opened_lot_entry_bar_mask`.

Four citation corrections the lanes asked the integrator for, in files main
owns after PR #272:

- The ten-significant-digit money residue was `engine.hpp:918-952`, not
  `:73-180` — measured by lane E6, which deleted it; `:73-180` is and was live
  code (`ClosedLotExcursionFacts`, `PyramidEntry`, `Trade`). Corrected in all
  four places that carried the wrong span: ADR-0001's "current state" bullet
  and its residue-table row 8, `docs/pine-adapter-kernel-notes.md:16`, and
  `native-feature-parity.md` rows (d) and 7.
- ADR-0001 said `<pineforge/native_c_api.h>` implements **32** additive
  symbols. Lane E11 added the 33rd.
- ADR-0001 recorded two rule-2 open questions. The wave closed both: the C
  delivery words are typed (lane E7's `pf_native_lookahead_e` /
  `pf_native_gaps_e`), and the excursion mask pair has no host writer (lane
  E6, with E11's C spelling and E15's declared-order derivation). Both bullets
  now say so, and say what is left: the pair is still durable hashed lot
  state, so removing it is still an epoch decision.

Also folded in, from the picks themselves rather than from a lane's own file
list: lane E14's new `##` ADR section was moved into the documentation wave's
"TradingView-calibrated kernel mechanisms" table B (a standalone section
beside the residual vocabulary reads as stale to
`scripts/check_kernel_residuals.py`), and the header's two asymmetry notes
were reconciled — `pf_native_working_v1` did grow a readout tail (E13), and
the trail seed is still deliberately not read back (E14).

Gates: `check_doc_anchors.py` 1555 anchors, bad=0 on every page;
`check_doc_lint.py` 0 offenders; `check_pine_to_native_coverage.py`
offered=131 covered=131; `check_design_inventory.py` 0 findings;
`check_native_feature_rulings.py`, `check_kernel_residuals.py` and
`check_native_c_api_surface.py` all clean.

One header edit rides here because it is the same kind of rot, caught by
`bash docs/build.sh` rather than by the anchor gate: lanes E7 and E11 wrote
their doxygen cross-references against the enum TAG (`#pf_native_lookahead_e`,
`#pf_native_gaps_e`, `#pf_native_opened_lot_fill_point_e`), while this header
indexes and every other row cites the typedef (`#pf_native_intent_t`,
`#pf_native_size_price_t`). Doxygen could not resolve the three, and E11's
block left `@param s` undocumented while documenting the other three
parameters. Four warnings; with the references pointing at `..._t` and `s`
named, `docs/build.sh` reports **0**.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Recomputed on the final tree, under lane Q12's rule: a floor is the number of
rows that RAN, never the number registered, and a skipped row is listed rather
than counted. No lane's own number is taken -- every lane raised its floor
against its own base (0a47cbf, 189/554), while this branch is built on the
documentation wave's main (9b50973, 193/558), so the lane values (E1 190/555,
E10 191/556, ...) are all relative to a tree that no longer exists.

Measured, not summed:

  python3 scripts/ci_verify.py kernel  --build-dir build-ci-kernel  --jobs 6
      ctest ran 199 rows (floor 193); not counted: 1 skipped
      (test_native_live_websocket)                    -> 200 registered
  python3 scripts/ci_verify.py release --build-dir build-ci-release --jobs 6
      ctest ran 566 rows (floor 558)                  -> 566 registered, none
      skipped

KERNEL_MIN_TESTS 193 -> 199, per lane:
  +1 E1   test_native_anchored_trail_absent_arm
  +1 E3   test_native_margin_fx_clock
  +1 E4   test_abi_receipt_skips        (registers in every profile)
  +1 E6   test_e6_entry_bar_mask_declaration
  +1 E10  test_e10_dead_path_prefix
  +1 E14  test_native_trail_best_seed   (kernel-only, unlike E5's and E9's)

RELEASE_MIN_TESTS 558 -> 566: those six, plus the two TUs that reach the
source layer and therefore register in release only:
  +1 E5   test_offset_trail_quantized_arm
  +1 E9   test_trail_activation_tick_reach

E2, E7, E8, E11, E12, E13 and E15 add no row: every witness they wrote is a
new scenario inside a TU that already existed (test_native_report_truth,
test_native_c_api, test_native_toolkit_bracket), which is why their lane
reports say the integrator adds nothing for them.

Cross-checked against a configure of origin/main's own tree with the same
options: 558 rows registered there, 566 here, and the diff of the two
`ctest -N` name lists is exactly those eight names with nothing removed.

The comment that records this arithmetic moves both constants inside
`scripts/ci_verify.py` (KERNEL_MIN_TESTS 93 -> 108, RELEASE_MIN_TESTS
116 -> 137), so the six citations of them in `CONTRIBUTING.md` and
`docs/pages/contributing-llm.md` are re-anchored here rather than in the
documentation commit: they are this commit's own line move.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
if in_block:
found = line.find("*/", at)
if found < 0:
at = end
luisleo526 and others added 2 commits September 22, 2026 09:36
CTest spells its closing summary two ways. CMake 3.28 -- ubuntu-24.04's apt
cmake, which the kernel-only CI job runs -- always prints the failed count:
"100% tests passed, 0 tests failed out of 4". CMake 4.x drops the clause when
nothing failed: "100% tests passed out of 4". The row matched the second
literally, so it passed on the local macOS host (cmake 4.4.0) and failed on
every Linux runner.

expectation corrected: the literal '100% tests passed out of 4' ->
ci_verify.ctest_rows() counts, because that string is the CMake >= 4 wording
only. ctest_rows is the parser the CI test floor already reads this same line
with, and it accepts both wordings; reusing it keeps one reader of the line
instead of a second copy of the regex.

CTestSummarySpelling pins both wordings as verbatim fixtures of the same four
rows -- one captured on cmake 3.28.3, one on cmake 4.4.0 -- so either host
covers the reader for both, without a Linux host.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
native_continuation_hash() is not portable. It folds the run's timezone
identity, and hash_tz_identity folds the host's zoneinfo ROOT PATH plus the tz
resource paths under it: tzdir_canonical() resolves /var/db/timezone/zoneinfo
on Darwin -- which realpaths through the installed tzdata release, e.g.
/private/var/db/timezone/tz/2026c.1.0/zoneinfo -- and /usr/share/zoneinfo, or
$TZDIR, under glibc. So the same kernel over the same bars, booking the same
rows at the same prices, answers a different digest per host. Measured: the
two controls answer 7148617029230866814 / 1059492877796320724 on Darwin and
3087945503312965943 / 5990633458242713781 on Linux (both aarch64, so it is
not the ISA), and on ONE Linux host copying /usr/share/zoneinfo elsewhere and
pointing $TZDIR at the copy moves every digest in this unit while every row,
exit price and position stays put.

expectation corrected: kControlContinuationA/B == the control's digest ->
same-host properties of the absent seed, because a continuation digest folds
a host path and cannot be a cross-platform literal. The removed values stay
in the note above the cases as the lane's recorded pre-field measurement
(exec/E14-probes/fail_before.cpp on the base 9f0ea24c), which is what carries
"absent hashes exactly as before" for the host that measured it.

What the literals were pinning is now pinned as properties, in a new case:
the two spellings of absent -- a request that never writes Trail::best_seed
and one that writes std::nullopt -- answer one digest; the digest is the
run's, not the process's; and a seed the run's own behaviour ignores (1.00,
under case A's arm print, so the ride still starts at the print and nothing
books) still moves it, which is what makes the seed request state rather than
an outcome. The seeded cases now compare against their own control's digest
instead of a literal. The printed digest lines stay: they are the
cross-platform measurement, and lane E17 needs them.

ADR-0001's covering-test cell said the controls carried the pre-field
digests; it now says what they carry and why.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@luisleo526
luisleo526 marked this pull request as ready for review September 22, 2026 02:31
@luisleo526
luisleo526 merged commit e9ad37d into main Sep 22, 2026
13 checks passed
luisleo526 added a commit that referenced this pull request Sep 26, 2026
…blings its leg had (R5 lane INT26, round 2: the gate sweep's leaver)

INT26's gate sweep (exp-r5-int26-20260926 against rebaseline e69aa1f7)
refused the merge on one leaver,
scrapper:data-BINANCE-BTCUSDT/standard/vasudevshenoy-manoj-betrayed-me
(BTCUSDT 15m, process_orders_on_close, pyramiding 0; stop entries
'Buy'/'Sell' with strategy.exit stop/limit brackets, a pending entry
cancelled when the next bar does not trigger it): 821 of TradingView's 821
trades on main 0d76a09, 820 on ebe6748 (canonical 100 -> 99.9).

Bisect (the probe's own feed, spark, one build per pick boundary): a6e6a00
and 2922ade book 821, e1d7a98 -- R5 lane PAR-ORDERS, H-MEASURE Finding 6b,
a process_orders_on_close stop entry its placing close already reached
fills at that close -- books 820, and so does every later pick.

Mechanism, on 2025-08-03 10:00 UTC. The 09:45 close cancels its pending
'Buy', places it again (stop 113689.29) and re-issues 'Buy Exit' (limit
113771.41); the re-issued legs keep their older book slot, so on the 10:00
bar the pre-script drain settles the limit on its parent's touch bar --
after that bar's receipt read. The 10:00 bar closes at its high 113771.47;
its body cancels 'Buy' again and places it at that high with a fresh 'Buy
Exit' (limit 114004.53), and since e1d7a98 the close pass fills that
re-entry at the close, as TradingView does (#274), so the fresh legs are
working when 10:15 opens. The first limit's terminal receipt is read only
then, and observe_terminal_receipts' sibling cancel withdrew every working
leg of the same exit id, from_entry and origin -- the fresh bracket's
included, both legs at origin 0. The re-entry ran on to the 15:15 session
close and swallowed TradingView's #275 (11:45). Before e1d7a98 the
re-entry rested to 10:15, its legs still staged, so the late read found no
sibling to take. The pick is right; the sibling cancel was not.

Fix, adapter only: cancel_bracket_siblings takes the ordinal the terminal
receipt executed at when the observer reads it, and skips a matching leg of
a later strategy.exit call (another command_sequence) that the kernel
accepted after that ordinal (its Birth::acceptance_ordinal, read through
native_working_requests() only when such a leg is among the matches): that
leg is the same id's later order. A leg of the filled leg's own call is its
sibling however late it was accepted -- the drain submits a bracket's legs
one at a time, executing each, so a stop can be accepted after its limit
filled -- and goes as before (a review of the first form, which skipped
every leg accepted after the fill, found that case). The close passes that
call it synchronously pass nothing and are unchanged. No state is added.

The probe (spark, generated.cpp of codegen a4259656, the lane feed
6b54c44a): 821 trades, canonical rubric excellent, countAbsDelta 0 (820 /
strong before). TradingView's #273-#275 exact: 10:00 113689.29 -> 10:00
113771.41; 10:00 113771.47 -> 11:00 114004.53; 11:45 114005.84 -> 15:15
113844.70. 819 of 821 trades exact to the minute and cent on both legs
(main: 771; the two others, #1 and #3, differ on main too).

Witness: tests/test_pooc_stop_reentry_bracket_tapes.cpp replays three `lab
tv --no-note` tapes of synthetic scripts of the same shape on NYSE:F 15m
(tests/fixtures/pooc_stop_reentry_bracket; tv_trades e0447b53 / b7e89ce1 /
efbd25e3): three flat signal closes that each cancel and place again a stop
entry with its bracket, 10c beyond the close, then 1c (the next bar fills
the entry and then the limit and closes flat), then at that close (filled
there), long on bars 39-41 and short on bars 44-46; TradingView books the
same four rows on the chart and under the magnifier. Every row is compared
field by field, with the end book and its working bracket legs.
- int26-reentry-bracket-chart: all four rows, flat; the short is the
  leaver's shape.
- int26-reentry-bracket-mag records a divergence the fix does not touch
  (the same before and after): there the drain leaves the short's retained
  limit to the flush below the body, whose book still holds the short, so
  the re-entry is never placed -- rows 1-3 exact, both books flat.
- int26-reentry-noexit-chart, the control: the short's re-entry places no
  strategy.exit (TradingView's row 4 still open at the end); the short
  bracket's stop, accepted after its limit filled, must still be withdrawn.
Fail-before: against ebe6748 2 of 27 checks fail (the chart tape: 3 rows
against 4, short 100 against flat); against the first form of the rule 1 of
28 (the control's stop left working); after, 28 of 28.

Floors: one source-bound row. RELEASE_MIN_TESTS 702 -> 703,
EXCLUDED_REGISTERED_MIN 683/683/692 -> 684/684/693 (and
test_ci_verify's expectation, docs/ci.md). KERNEL_MIN_TESTS unchanged.
docs/ci.md's sentence "The PR registration floors at INT26 are 683 for
Debug and sanitizers and 692 for native" is rewritten with the new counts.

Docs: the design page's PAR-ORDERS paragraph records the rule and the
magnified divergence; the CHANGELOG 1.0.0 entry's process_orders_on_close
item adds it for a user. Every doc anchor carried onto this tree by the
exact line map from ebe6748 (135 moved); the three citations of
ci_verify.py's RELEASE_MIN_TESTS line (CONTRIBUTING.md, contributing-llm.md
twice) set by hand, 390 -> 393.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@luisleo526
luisleo526 deleted the r5/int13-wave-d branch October 5, 2026 02:43
luisleo526 added a commit that referenced this pull request Oct 29, 2026
…nnel failure codes, live runner report checkpoints (#637)

Engine changes for 1.9.0.

## Members

- Closed-trade text reads: compare closed-trade text in place, read sealed rows, and cheapen the read hit path; the owning text read is left to the compiler's inlining.
- Build: the shared window-plan library links on macOS.
- Run quota contract: host ledger, wire format and typed failure codes, with the ownership manifest naming their release.
- Failure codes for the planned supervised report channel.
- Live runner: native report checkpoints and the carried report accumulator; the webhook worker gets a dedicated curl-owner thread so delivery progress does not wait for the ledger; documentation corrections after review.
- The run-failure registry, header and catalog diff are regenerated by the repository's generator. The ten failure codes added in 1.9.0 are numbered 70 to 79 in name order (the registry count is 80); none of these numbers was released before.
- The allocation-bound inventory records the run-quota source sites.

## Sentences rewritten on purpose

In docs/pages/run-failure-codes.md, "The C++ registry also contains one unnamed `none` sentinel, giving 70 registry entries." (introduced by 91353de) is replaced by the catalog count paragraph (79 named codes, 80 registry entries). In docs/live-ledger-budget.md, "Optional `extra_payload_bytes` margin supplied by another writer; zero for the current writers." and "The optional extra-payload margin is additive; a writer must still put its SQL changes in the admitted transaction." (both introduced by a991b4e) are rewritten because the margin now has a defined meaning.

## Verification

Release, debug, kernel and sanitizer builds with their full test suites, the native live suites, the corpus subset and parity all pass on this exact head. Each member was reviewed independently.

Pairs with the code generator change in pineforge-codegen-oss #273, which merges after this one.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant