Repository navigation
engine 1.9.0: closed-trade text reads, run quota contract, report channel failure codes, live runner report checkpoints - #637
Conversation
…apen the read hit path Add entry_id_equals, exit_id_equals, entry_comment_equals and exit_comment_equals on TradeLedger and protected closed_trade_*_equals engine wrappers: one checked lookup, a byte comparison against the cached text, and a single fixed stack chunk for rows whose text is not cached; the owning getters are unchanged. Closed-trade reads of the sealed rows of a non-retiring ledger now return the stored row instead of refusing: the numeric and text field reads, at(), numeric_at(), back() and the close-cause query behind the C string accessors, which return the stored text instead of NULL. Writers still refuse sealed rows, rows at or past the end stay refused, and a ledger whose prefix the sink physically erased still refuses with the typed retired-history error. The range check of a read keeps its throw in a cold out-of-line helper and inlines into the field accessors, which probe the decoded cache once. No class layout changes: no member is added to any type a compiled strategy module sees, so the C ABI version and every module built for 1.8.0 stay valid. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The window-plan shared library links the kernel archive, which keeps a weak reference to the optional capability a generated strategy module may define (strategy_reads_trade_history). macOS refuses an undefined symbol in a shared library, so 1.8.0 did not link there. On Apple platforms only, allow exactly that one symbol to stay undefined for this target; every other missing symbol is still a link error. The statement sits after every cited CMake line so no documentation anchor moves. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ining The forced-inline attribute on the owning field read changed the lexical identity of its string-temporary site in the allocation inventory for no measured gain; the compiler already inlines the small hit path. The in-place comparison and the numeric read keep their attributes. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Documentation sentence rewritten on purpose: in docs/pages/run-failure-codes.md the sentence "The C++ registry also contains one unnamed `none` sentinel, giving 70 registry entries." (introduced by 91353de) is replaced by the catalog count paragraph, which now states the number of named failure codes and the number of registry entries for this tree because the catalog gains three codes. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The catalog now states the three codes first ship in 1.9.0; the selected-window ownership manifest mirrors the catalog's since field and still said 1.8.0. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ist grew by one line The kernel source list gained one entry, moving add_library from line 172 to 173 and add_subdirectory from 406 to 407; seven citations in three pages follow. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…l (73-79) The codes are appended after the run-quota codes, documented with the resource-measurement allowance, listed in the ownership manifest and pinned by tests. Nothing emits them and no existing code, argument or exit status changes. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ator The live runner writes immutable cumulative report checkpoints at K, 2K, 4K, ... committed inputs (K defaults to 1024; --checkpoint-interval sets it) and reads the nearest verified checkpoint plus its tail for export and restart verification, falling back to full reconstruction when a checkpoint is missing, corrupt or over the 1 MiB parser limit (documented in the runner README). Ledger admission reserves the serialized checkpoint payload before the input commit and records a typed diagnostic when an optional checkpoint cannot be written. Report production uses a carried accumulator for modules that export the engine-layout stamp, which keeps the per-input report work proportional to the new rows (the exact ratios stay exact: O(series) per input at the default interval, at the scheduled boundaries with --ratio-interval K); modules without the stamp keep the public-report path, so no unrelated state is ever cast to the engine type. The accumulator keeps the exact-ratio curve as a persistent workspace instead of copying the whole curve at every ratio boundary. The ledger layouts the runner accepts follow from one list of its tables, an old ledger gets its first checkpoint on open, and the report CLI gains --verify-full. The commit also carries the tests and tooling of the feature: the checkpoint, accumulator, layout-stamp and memory rows (the memory row's work deadline and ctest timeout follow the build kind, and under the address sanitizer the child runner gets quarantine_size_mb=0 merged into the caller's options so peak memory measures live retention), a golden captured from main's runner for an opaque-module report, a count-based row for the curve staging, and a harness change that prints the ledger evidence of an unacknowledged event when a live equivalence scenario ends with one. The runner README, the changelog and the documentation counts are updated, including the one new runtime export (strategy_stream_engine_layout_version, 98 runtime implementations, 118 declarations). Two documentation sentences in docs/live-ledger-budget.md, introduced by a991b4e, are rewritten on purpose because the margin they describe now has a defined meaning: the table row "Optional `extra_payload_bytes` margin supplied by another writer; zero for the current writers." becomes the serialized cumulative report checkpoint length for a scheduled boundary input, and the sentence "The optional extra-payload margin is additive; a writer must still put its SQL changes in the admitted transaction." is extended with the boundary-input admission and checkpoint-transaction rules that follow from it. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
… delivery progress does not wait for the ledger The delivery worker constructed the curl multi handle, added requests, polled and persisted results on one thread, so every ledger call, including one that waits while the input thread holds the ledger mutex through a checkpoint, stopped curl from making progress. A response the receiver had already accepted could then be reported as a timeout, recorded as a failed delivery and sent again. A second thread now owns curl: it alone constructs, adds, polls and destroys the transport, exchanging data with the worker through two preallocated single-producer single-consumer rings of 256 entries with release/acquire publication, no lock and no wait on the other side. The worker keeps every ledger operation and the existing lock order: it reserves command space and a result credit before the started-row transaction, publishes the command after the row commits, and releases the credit only after the atomic finish and the retry decision. Active work covers commands not yet added, live handles and results awaiting the ledger, so a drain cannot end before every result is persisted; the finish request is still read before the ledger scans; cancellation and the drain deadline stop the owner from submitting queued sends; an exception in the owner is published once and surfaces through the existing worker-failure path; every exit stops and joins the owner after the ledger scopes have unwound. Acknowledgement still comes only from the atomic finish transaction, crash recovery is unchanged, and no timeout, retry, classification, store or checkpoint code changed. A count-based row drives the real worker over a fake transport and a real ledger and requires, by thread identity and operation counts rather than elapsed time, that one transport thread distinct from the ledger-facing thread owns the transport, that scripted polls complete requests while the worker is parked, a 256-result backlog, cancellation with a queued result, an exception in the transport thread, and a retry under a new key. The service test's fake now returns no result until all four keys of its stop case exist, keeping every existing assertion. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…hten three small details Documentation: the runner README and the changelog no longer say the C exports are unchanged (the runtime surface gains the optional strategy_stream_engine_layout_version query) and now say which modules the carried accumulator and the ratio interval apply to: modules whose export matches the runner's layout stamp; a module without a matching stamp keeps the public-report path with exact ratios at every input and no interval marker, and the runner prints no warning about it. The changelog gains an entry for the dedicated curl-owner thread, stating the up-to-20 ms wait each direction can have for the other thread to notice work, that ended_at is stamped when the result is recorded in the ledger (after a ledger stall a successful attempt can show a duration above the total timeout), and that timeouts, retries, acknowledgement and recovery are unchanged. The README states exactly what a cancellation discards, that a report checkpoint over 1 MiB is still written, can never be read back and counts against the storage budget (reports are then rebuilt from the report rows), and the build-kind budgets of the report-memory row replace the old statement of a single 180 s timeout; the stream family is 13 symbols. Code and build: the curl-owner thread's command slot is declared inside its batch loop so an emptied slot does not keep a moved-from payload alive, test_native_live_service gets a 180 s ctest timeout (its four-result wait would otherwise hang when it regresses), a CMake comment now matches the plain-Debug budget, and the live equivalence harness's evidence printer can no longer mask the scenario's own failure. Several README, changelog and documentation sentences are rewritten on purpose by this commit to make them true for the carrier as described above. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Regenerate include/pineforge/run_failure_codes.hpp, src/run_failure_registry.inc and docker/run_failure_codes_diff.json from the merged run-quota and report-channel catalog with the repository's own generators, on a clean clone with release tags. The ten codes added after 1.8.0 now follow the generator's numbering rule (numeric since, then name); no number below 70 changes. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…ded in 1.9.0 The ten codes added after 1.8.0 all carry since 1.9.0, so the registry numbers them by name (70 to 79). Update the number-pin test to that group and the two number sentences in the run-failure code page; the test no longer lists the run-quota codes under 1.8.0, which shipped without them. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The failure-code preamble check matches each release group as "N added in X.Y.Z" or "N first released in X.Y.Z" and compares the groups with the catalog's introduction versions. The 1.9.0 group of ten codes was written as "scheduled for 1.9.0", so the group was missing and test_s4e_misc_contracts failed. Use the matching wording. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Add the four container groups the source gate found in src/run_quota.cpp (the failure-argument list, the endpoint map and its subscript, and the two shared allocations) as unreviewed OPEN entries, raise the unreviewed ceiling by those four, and refresh the site inventory. Both files are the gate's own output (--emit-seed and --emit-site-inventory) from a clean full clone; no bound is claimed. The inventory generator sorts globally, so the existing live-report rows move to their sorted position with their content unchanged. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The real-tree gate test pins the number of OPEN entries. The run-quota sources add four unreviewed entries, so the pin moves from 1518 to 1522, the count the gate reports on the patched tree. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
| descriptors.remove(native.fileno()) | ||
| native.close() | ||
| elif fault == "regular": | ||
| backing = extra.enter_context(open(tmp_path / "non-socket", "w+b")) |
| def run(): | ||
| try: | ||
| q.serve(ledger, channel, actor) | ||
| except BaseException as error: |
| generation=1, new_extent=70)) | ||
| except q.QuotaError as error: | ||
| denied.append(error) | ||
| except BaseException as error: |
| values[3] = 0 | ||
| frame = FRAME.pack(b"PFQ1", 1, 0x8001, 1, *values) | ||
| host.sendall(frame) | ||
| except BaseException as error: |
| if hasattr(os, "pidfd_open") and hasattr(select, "poll"): | ||
| try: | ||
| proof = _ProcessProof(pidfd=os.pidfd_open(pid, 0)) | ||
| except (OSError, OverflowError, NotImplementedError): |
| try: | ||
| if proof.pidfd is not None: | ||
| os.close(proof.pidfd) | ||
| except OSError: |
| for state in self._owners.values(): | ||
| state.connected = False | ||
|
|
||
| def __del__(self): |
There was a problem hiding this comment.
Graphify reviewed this change.
No issues found by static checks — no coupling regressions or blocking issues in the code graph. That is not the same as safe to merge: see what was not checked.
Not checked: tests were not run; formal verification proved 0 of 19 changed function(s) (19 not verified).
Formal verification. PR-changed functions: 0/19 verified (0 proven, 0 may-equivalent, 0 distinguished) · 19 not verified (19 unsupported).
Not verified on this run: args (unsupported), help (unsupported), ledger\_command (unsupported), run (unsupported), apply\_report\_delta (unsupported), commit\_input (unsupported), report\_json (unsupported), at (unsupported), back (unsupported), materialize (unsupported), and 9 more.
Graphify review — findings
Adds immutable cumulative report checkpoints at K, 2K, 4K, … committed inputs (--checkpoint-interval, default 1024) to the native live runner, so export and restart verify the report chain only from the nearest checkpoint. report --verify-full re-verifies the whole chain, and a payload or witness failure falls back to full reconstruction. Old ledgers get their first checkpoint at open, and a failed checkpoint leaves the input durable and records a diagnostic. Moves webhook HTTP onto a dedicated curl-owner thread, linked to the delivery worker by two 256-entry rings, so a ledger-mutex stall during a checkpoint no longer times out accepted deliveries. ended_at is stamped when the result is recorded, so after a stall it can exceed total_timeout_ms. The report stream now defaults to delta JSONL (--report-jsonl=full keeps the old output), and --ratio-interval K trades per-input ratio freshness for work on modules that export a matching engine-layout stamp. Also adds allocation-free in-place comparison of closed-trade ids and comments, serves sealed rows of non-retiring ledgers from retained storage, registers seven failure codes that nothing emits yet, builds run_quota.cpp into the kernel, and lets the macOS window-plan library leave strategy_reads_trade_history undefined at link time.
Review partial — this diff was larger than one review pass covers, so later files were not reviewed; some findings may be missing.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 7665 functions depend on the 4495 functions this change touches.
Health — this change adds coupling hotspots:
- worse:
commit_input— 32 callers, 15 callees - worse:
verify_report— 18 callers, 11 callees - worse:
run()— 1 callers, 71 callees - worse:
input— 17 callers, 4 callees - worse:
make_run_failure()— 8 callers, 8 callees - new:
create_history()— 10 callers, 6 callees - new:
native_clients()— 9 callers, 6 callees - worse:
note_run_failure()— 17 callers, 3 callees - …and 73 more — each is listed as a finding
Verification — 7665 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 7625 function(s) in the blast radius were not formally verified this run
Risky patterns (advisory)
- medium
no-unbounded-fetchindocker/run_quota.py:554: unbounded read (no limit/bound): record = source.read() - medium
no-unbounded-fetchindocker/run_quota.py:565: unbounded read (no limit/bound): boot_id = str(UUID(source.read().strip()))
Docs that may be stale (advisory)
AGENTS.md§ REQUIRED before claiming any change is done (lines 5-45): references changed symbolsreleaseAGENTS.md§ SOP: adding a runtime PF_API export (CI gate — recurring failure) (lines 73-97): references changed symbolsrunAGENTS.md§ Refactor workers (native-engine programme, R4 and later) (lines 105-141): references changed symbolsreleaseCLAUDE.md§ REQUIRED before claiming any change is done (lines 5-48): references changed symbolsrunCLAUDE.md§ SOP: adding a runtime PF_API export (CI gate — recurring failure) (lines 76-100): references changed symbolsrunCLAUDE.md§ Refactor workers (native-engine programme, R4 and later) (lines 108-143): references changed symbolsreleaseCONTRIBUTING.md§ The gates (lines 123-158): references changed symbolscheck,releaseCONTRIBUTING.md§ Coverage (lines 272-286): references changed symbolsreportCONTRIBUTING.md§ Maintainer: release checklist (lines 346-368): references changed symbolsreleasebenchmarks/README.md§ Reproduce (lines 82-145): references changed symbolsrun
…and 10 more.
Formal verification
Could not verify: Could not verify args.
The verifier did not have enough to check args, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported return type: Config
Could not verify: Could not verify help.
The verifier did not have enough to check help, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported return type: void
Could not verify: Could not verify ledger\_command.
The verifier did not have enough to check ledger\_command, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported parameter type(s)
Could not verify: Could not verify run.
The verifier did not have enough to check run, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported parameter type(s)
Could not verify: Could not verify apply\_report\_delta.
The verifier did not have enough to check apply\_report\_delta, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported return type: void
Could not verify: Could not verify commit\_input.
The verifier did not have enough to check commit\_input, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported: member function — receiver/self (`this`) not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver
Could not verify: Could not verify report\_json.
The verifier did not have enough to check report\_json, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported: member function — receiver/self (`this`) not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver
Could not verify: Could not verify at.
The verifier did not have enough to check at, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported: member function — receiver/self (`this`) not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver
Could not verify: Could not verify back.
The verifier did not have enough to check back, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported: member function — receiver/self (`this`) not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver
Could not verify: Could not verify materialize.
The verifier did not have enough to check materialize, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported: member function — receiver/self (`this`) not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver
Could not verify: Could not verify numeric\_at.
The verifier did not have enough to check numeric\_at, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported: member function — receiver/self (`this`) not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver
Could not verify: Could not verify seal.
The verifier did not have enough to check seal, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported: member function — receiver/self (`this`) not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver
Could not verify: Could not verify closed\_trade\_close\_cause.
The verifier did not have enough to check closed\_trade\_close\_cause, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported: member function — receiver/self (`this`) not synthesizable from the simple-type literal pools — abstaining rather than guessing a receiver
Could not verify: Could not verify classify\_run\_failure.
The verifier did not have enough to check classify\_run\_failure, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported return type: RunFailureValue
Could not verify: Could not verify make\_run\_failure.
The verifier did not have enough to check make\_run\_failure, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported return type: RunFailureValue
Could not verify: Could not verify note\_run\_failure\_unknown.
The verifier did not have enough to check note\_run\_failure\_unknown, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported return type: void
Could not verify: Could not verify run\_failure\_args\_of.
The verifier did not have enough to check run\_failure\_args\_of, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported return type: char*
Could not verify: Could not verify run\_failure\_code\_of.
The verifier did not have enough to check run\_failure\_code\_of, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported return type: char*
Could not verify: Could not verify run\_failure\_value\_of.
The verifier did not have enough to check run\_failure\_value\_of, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: unsupported return type: RunFailureValue
· 20 grounded finding(s) anchored inline below; 14 more finding(s) on lines outside this diff (see the check run); 47 additional anchorable finding(s) not shown (cap).
| channel.setsockopt(socket.SOL_SOCKET, socket.SO_PASSCRED, 1) | ||
|
|
||
|
|
||
| def serve(ledger, channel, owner): |
There was a problem hiding this comment.
serve()
fans out to 11 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| std::string payload; | ||
| }; | ||
|
|
||
| std::optional<std::string> stored_report_digest(sqlite3* database, std::uint64_t cursor) { |
There was a problem hiding this comment.
stored_report_digest()
high coupling complexity (Ca·Ce = 12).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| return CheckpointRow{query.integer(0), query.text(1), query.text(2), query.text(3), query.text(4)}; | ||
| } | ||
|
|
||
| Json verify_checkpoint(sqlite3* database, const CheckpointRow& checkpoint, |
There was a problem hiding this comment.
verify_checkpoint()
fans out to 7 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| document = std::move(payload); | ||
| } | ||
|
|
||
| Json read_full_report(sqlite3* database, std::uint64_t target, ReportReadStats* stats) { |
There was a problem hiding this comment.
read_full_report()
fans out to 7 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| @@ -91,6 +145,11 @@ class Ledger { | |||
| const std::string& report_json = {}); | |||
| void verify_report(std::uint64_t cursor, const std::string& report_json, | |||
There was a problem hiding this comment.
verify_report
fans out to 11 callees (efferent coupling); 18 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| check_summary(ending.substr(newline + 1), count, count); | ||
| } | ||
|
|
||
| void ratio_marker_replay(Fixture& fixture) { |
There was a problem hiding this comment.
ratio_marker_replay()
fans out to 16 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| } | ||
| } | ||
|
|
||
| void ratio_body_replay(Fixture& fixture) { |
There was a problem hiding this comment.
ratio_body_replay()
fans out to 16 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| CHECK_OP(LedgerView(path).report_json(3) == "{}", ==, true); | ||
| } | ||
|
|
||
| void startup_and_recovery_checkpoint_budget() { |
There was a problem hiding this comment.
startup_and_recovery_checkpoint_budget()
fans out to 14 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| unacknowledged(path, 2, 2); | ||
| } | ||
|
|
||
| void retry_after_durable_failure(const fs::path& root) { |
There was a problem hiding this comment.
retry_after_durable_failure()
fans out to 13 callees (efferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
|
|
||
|
|
||
| @contextmanager | ||
| def native_clients(q, program, *ledgers): |
There was a problem hiding this comment.
native_clients()
fans out to 6 callees (efferent coupling); 9 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
….0 (#639) Changelog corrections before the 1.9.0 release. Documentation only (`CHANGELOG.md`). - The live runner's report stream default is stated correctly: plain `--report-jsonl` and `--report-jsonl=full` keep emitting the cumulative document; `--report-jsonl=delta` is opt-in. The previous text called delta the default, and an introductory sentence referred to a later entry that does not exist; both are removed (the commit message quotes them). - The 1.9.0 section gets its dated heading. - One line notes that the window-plan library now links on macOS (the 1.8.0 known issue); CI covers Linux only. Replaced sentences, named for the documentation-revert check (all in CHANGELOG.md on main, from the 1.9.0 squash of #637, and replaced here): - "The development entries below are retained in order. The cumulative stream default in the later entry supersedes the earlier delta default; the carried accumulator in the earlier entry closes the later entry's remaining native rebuild term." - "Delta JSONL is the default. `pineforge-live run --report-jsonl` now emits one `pineforge-native-report-delta/v1` document for each committed input, matching the payload stored in the ledger. This deliberately changes the stream shape for existing consumers that read the old cumulative document; request `--report-jsonl=full` to retain that cumulative output." 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Engine changes for 1.9.0.
Members
Sentences rewritten on purpose
In docs/pages/run-failure-codes.md, "The C++ registry also contains one unnamed
nonesentinel, 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, "Optionalextra_payload_bytesmargin 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