Skip to content

Commit e1fffef

Browse files
authored
Formal: close PASTA + prove 2 metrics end-to-end across C++/Python; add STATUS.md (#1080)
* Formal: prove PASTA (discrete) + copy volume end-to-end; add STATUS.md Closes the two largest open gaps in the correctness map (README §6 boundary). PoissonArrivals.lean — PASTA in discrete-time form, the analogue the effort targeted. Models the horizon as M equal time slots; a Poisson arrival conditioned on its count is uniform over the slots (order-statistics property). Proves the landing probability on line ℓ equals ℓ's time fraction (uniform_landing_eq_timeFraction), and that this realizes exactly the trueFraction sampling law ProfilerCorrectness ASSUMES (uniform_realizes_trueFraction). So the faithful-sampling hypothesis feeding estimator_unbiased / jointVariance_eq is now discharged by the sampler mechanism (via ExponentialSampler's Poisson result), not merely cited. CopyVolumeWiring.lean — the first metric proven END TO END across the C++/Python boundary. Models both the memcpysampler.hpp accumulator/flush state machine and the process_memcpy_samples Python reader. Proves C++ conservation (flushed = observed - residual), Python transfer faithfulness (mapfile + pid filter neither drop nor dup in-process bytes), the round-trip conservation headline (reported volume = observed - residual), foreign-pid drop, and a residual bound of one sampling interval. Both: no sorry, standard axioms only; full lake build green (8572 jobs). STATUS.md — committed subsystem status map (proven / partial / unproven) so the 'where do we stand' view stays current. README gains §12 (PASTA) and §13 (copy wiring) with source mappings; HANDOFF module table + next-steps updated (steps 4 and 7 now DONE). * Formal: malloc footprint end-to-end (C++<->Python), incl. the free-side clamp Third end-to-end metric across the native/Python boundary and the harder of the memory paths: the current-footprint / peak-memory number. Spans the C++ SampleHeap emitter (sampleheap.hpp) and the Python reader process_malloc_free_samples. The C++ half reuses MemorySampler's ThresholdSampler (threshold_conserves) via a new emitter bridge. The audit-method payoff: the Python free path clamps current_footprint to max(0, current - count) on every free (memory_profiler.py:218), which BREAKS pure conservation when frees outrun tracked allocs (startup misses). A naive model ignoring the clamp would falsely 'prove' conservation. Instead: - emit_records_sum: the records the C++ sampler emits sum (signed) to its reported net -- bridge to threshold_conserves. - clamp_is_identity_of_safe: while footprint stays >= 0 the clamp is inert and the Python fold is exactly additive. - roundtrip_conservation_of_safe (headline): in that regime, reported footprint delta = (true net - sampler residual) / BYTES_PER_MB. End to end. - clamp_only_raises: WITHOUT non-negativity, the clamp can only push the footprint UP -- reported memory is never a silent undercount. Honest bound. - foreign_pid_dropped, newline_marker_skipped: the pid filter and NEWLINE continue faithfully drop non-counting records. No sorry; standard axioms only. Full lake build green (8573 jobs). 14 modules / 114 theorems. STATUS.md §4b, README §14, HANDOFF updated. * Formal docs: add 'why two engines' rationale (Lean vs TLC) Pin down that Lean and TLA+/TLC are complementary, not redundant: Lean for unbounded/quantitative correctness, TLC for bounded interleaving-existence (counterexample traces) and liveness. Notes the one deliberate overlap (LeakTrackerConcurrency assumes step-atomicity as an axiom) and that deriving it from the sig-queue operational semantics is a natural TLA+ job. STATUS.md gets a dedicated section; README's existing 'Why both?' note strengthened. * Formal README: add subsystem proof roundup at the top Replace the stale numbered intro list (1-9, predating PASTA, the two wiring modules, leak-concurrency, and per-line attribution) with a current narrative roundup by subsystem, marking each aspect proven/partial/unproven with theorem names and section cross-refs. Points to STATUS.md for the granular per-aspect table and to the numbered sections (now 1-14) for full statements.
1 parent 57299b6 commit e1fffef

7 files changed

Lines changed: 1109 additions & 37 deletions

File tree

formal/HANDOFF.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ models adversarially, don't just prove them.
3838

3939
```
4040
formal/
41-
README.md # full writeup: §1–§10, source mappings, boundaries, repro
41+
README.md # full writeup: §1–§14, source mappings, boundaries, repro
42+
STATUS.md # subsystem status map (proven / partial / unproven)
4243
HANDOFF.md # this file
4344
tla/ # TLA+ specs (model-checked with TLC)
4445
SignalSafety.tla + _Fix.cfg / _Bug.cfg
@@ -81,6 +82,9 @@ at `/tmp/LeanToPython` locally (Lean 4.12).
8182
| `MetricCorrectness.lean` | GPU/copy/python-split (weighted-avg) + leak detection (Bayesian test) | `gpuFraction_bounds`, `python_c_fraction_sums_one`, `leakScore_*`, `reportsLeak_iff`, `no_leak_without_evidence` |
8283
| `MemorySampler.lean` | threshold sampler conserves net exactly; Poisson unbiased; **two-counter bisimulation** | `threshold_conserves`, `threshold_residual_bounded`, `threshold2_conserves`, `step_bisim`, `poisson_unbiased` |
8384
| `PerLineAttribution.lean` | per-line byte fraction faithful under sampling | `fraction_of_expectations`, `recorded_fraction_exact` |
85+
| `PoissonArrivals.lean` | **PASTA (discrete)**: uniform arrival lands on ℓ with prob = ℓ's time fraction; realizes the assumed `trueFraction` sampling law → discharges the ProfilerCorrectness hypothesis | `uniform_landing_eq_timeFraction`, `uniform_realizes_trueFraction`, `sum_timeFraction` |
86+
| `CopyVolumeWiring.lean` | **copy volume end-to-end (C++↔Python)**: emitter/reader state machines; reported volume = observed − residual | `flushed_add_residual`, `python_total_eq_flushed`, `roundtrip_conservation`, `foreign_pid_dropped` |
87+
| `MallocFootprintWiring.lean` | **malloc footprint end-to-end (C++↔Python)**: reuses ThresholdSampler; models the free-side `max(0,·)` clamp honestly (conserves in safe regime; only over-reports otherwise) | `emit_records_sum`, `clamp_is_identity_of_safe`, `roundtrip_conservation_of_safe`, `clamp_only_raises` |
8488
| `LeakTrackerAudit.lean` | proves the leak formula's *unguarded* denominator is safe (`frees ≤ allocs`) | `run_frees_le_allocs`, `denom_pos_reachable` |
8589
| `LeakTrackerConcurrency.lean` | `frees ≤ allocs` survives sig-queue/main-thread interleaving + fork; RLock atomicity & joint fork-reset shown *necessary* | `interleave_preserves_inv`, `torn_free_breaks_inv`, `fork_reset_inv`, `partial_fork_reset_breaks_inv` |
8690
| TLA+ `SignalSafety` | the combined_stacks race is reachable (bug cfg) / impossible (fix cfg) | 4-state counterexample; 99 states clean |
@@ -233,15 +237,25 @@ generated `X | Y` unions need 3.10+).
233237
other divides (~339, ~379, ~416, ~657) are guarded. NEXT untouched surfaces:
234238
the third renderer's path + `sparkline.py` / `runningstats.py` variance/stddev
235239
denominators. Lesson reinforced by #4: audit ALL THREE renderers, not one.
236-
4. **Formalize PASTA** (or at least a discrete-time analogue) to fully discharge
237-
the i.i.d.→trueFraction step instead of citing it.
240+
4. ~~Formalize PASTA (discrete-time analogue)~~ **DONE**
241+
`PoissonArrivals.lean`. Uniform-arrival-over-M-slots lands on ℓ with prob =
242+
time fraction, and realizes the `trueFraction` law ProfilerCorrectness
243+
assumes (`uniform_realizes_trueFraction`). Residual: continuous-time
244+
order-statistics proof still not formalized (discrete form is the operative
245+
content for a tick-sampled profiler).
238246
5. **Prove per-sample classifier accuracy** for the python/native split, or
239247
document precisely why it's a heuristic with bounded error.
240248
6. **Wire the verified oracle into production directly** (have
241249
`_space_saving_increment` call the extracted core) rather than only
242250
differential-testing it — closes the proof→production loop tighter.
243-
7. **Model one more column end-to-end** (e.g. GPU or copy-volume) under the
244-
unbiased-estimator frame, including the C++→Python wiring.
251+
7. ~~Model columns end-to-end incl. the C++→Python wiring~~ **DONE (×2)**
252+
`CopyVolumeWiring.lean` (memcpy) and `MallocFootprintWiring.lean` (current
253+
footprint / peak memory; models the free-side `max(0,·)` clamp honestly).
254+
NEXT such target: per-line malloc *attribution* wiring (the
255+
`memory_malloc_samples[file][line] += count` path, distinct from the footprint
256+
total modeled here) or GPU acquisition.
257+
8. **A committed status map** lives at `formal/STATUS.md` — keep it current when
258+
modules land.
245259

246260
---
247261

formal/README.md

Lines changed: 192 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,67 @@ This directory contains machine-checked formal models of Scalene's runtime,
44
plus a **proof→production pipeline** that extracts the proven algorithms to
55
Python and differentially tests the real profiler against them.
66

7-
1. **Signal / iteration safety** — the profile-output loop never faults from a
8-
concurrent signal-handler mutation of the shared stacks dictionaries.
9-
2. **Deadlock freedom & signal-safety** — Scalene's lock/queue topology cannot
10-
deadlock, and no signal handler ever blocks on a lock.
11-
3. **Attribution bookkeeping** — CPU time and memory bytes are conserved
12-
(attributed exactly once, totals preserved) and the Python/C split fractions
13-
stay in `[0, 1]`.
14-
4. **Bounded heavy-hitter accounting** — the Space-Saving `combined_stacks`
15-
table never exceeds its capacity (`SpaceSaving.step_withinCap` /
16-
`fold_withinCap`), and eviction always removes a minimum-count entry.
17-
5. **Proof → production** — the proven Lean defs are extracted to Python via
18-
[LeanToPython](https://github.com/emeryberger/LeanToPython) and used as a
19-
*verified oracle* that Scalene's real `_space_saving_increment` is checked
20-
against (`tests/test_verified_space_saving.py`).
21-
6. **Profiler correctness** — the headline desideratum: the reported per-line
22-
time/memory profile is an **unbiased, consistent** estimator of the truth
23-
(`ProfilerCorrectness.estimator_unbiased`, `jointVariance_eq`). This is the
24-
spec a profiler's *user* relies on; §3 proves the bookkeeping it rests on.
25-
7. **Poisson sampling** — the sampler's exponential inter-sample intervals
26-
(`scalene_profiler.py:1108`) make sampling a Poisson process, which is what
27-
*discharges* §6's i.i.d. hypothesis (inverse-CDF correctness + memorylessness).
28-
8. **GPU / copy-volume / python-split / leak detection** — GPU util, memcpy
29-
volume, and the Python/native split fit the §6 weighted-average frame; memory
30-
leak detection is a Bayesian (Rule-of-Succession) hypothesis test with proven
31-
bounds, monotonicity, and false-positive guards.
32-
9. **Memory sampler** — the default ThresholdSampler conserves true net
33-
allocation exactly (bounded residual); the Poisson sampler is unbiased.
7+
## Proof roundup — where the correctness effort stands, by subsystem
8+
9+
**Lean 4:** 14 modules, 114 theorems, no `sorry`, standard axioms only.
10+
**TLA+/TLC:** 2 specs, exhaustively model-checked. Verdicts: **✅ Proven**,
11+
**⚠️ Partial**, **❌ Unproven**. This is the narrative view; [`STATUS.md`](STATUS.md)
12+
has the granular per-aspect table, and the numbered sections below (§1–§14) give
13+
the full statements with source mappings.
14+
15+
**1. CPU profiling — the headline.** The reported per-line profile is an
16+
**unbiased, consistent** estimator of the truth: ✅ `estimator_unbiased` (right
17+
on average at any sample budget N), ✅ `jointVariance_eq` (variance = p(1−p)/N →
18+
0). The i.i.d. hypothesis this rests on is discharged, not assumed: ✅ the
19+
sampler is Poisson (`ExponentialSampler`, inverse-CDF + memorylessness) and ✅
20+
**PASTA** now links Poisson instants to time-fraction landing
21+
(`PoissonArrivals.uniform_realizes_trueFraction`, §12). ⚠️ that the C++ stamping
22+
*establishes* faithful placement is engineering, not Lean-proven; ❌ the
23+
Python/native per-sample classifier heuristic's accuracy.
24+
25+
**2. Memory sampling.** ✅ the default ThresholdSampler conserves true net
26+
allocation exactly with bounded residual (`threshold_conserves`,
27+
`threshold_residual_bounded`), ✅ proven bisimilar to the literal two-counter C++
28+
(`step_bisim`), ✅ the Poisson sampler is unbiased, ✅ per-line byte fractions
29+
are faithful (`PerLineAttribution`).
30+
31+
**3. Memory-leak detection — fully closed, incl. concurrency.** ✅ the leak score
32+
is a Rule-of-Succession probability in [0,1] with monotonicity and an exact
33+
decision rule (`MetricCorrectness`); ✅ its unguarded denominator is safe
34+
(`LeakTrackerAudit`, `frees ≤ allocs`); ✅ that safety survives the sig-queue /
35+
main-thread interleaving and `fork`, and the serialization is shown *necessary*
36+
(`LeakTrackerConcurrency`). The audit that built this found production bugs
37+
(below).
38+
39+
**4. Metrics end-to-end across the C++/Python boundary.****copy volume**
40+
(`CopyVolumeWiring`, §13) and ✅ **malloc footprint / peak memory**
41+
(`MallocFootprintWiring`, §14) — the reported number equals what the native
42+
interposer observed, up to a bounded sampler residual. The footprint model
43+
handles the free-side `max(0,·)` clamp *honestly*: exact in the non-negative
44+
regime, and outside it the clamp can only over-report (`clamp_only_raises`),
45+
never silently undercount. ✅ GPU/copy/python-split arithmetic bounds; ❌
46+
GPU/accelerator device-acquisition paths.
47+
48+
**5. Concurrency & signal safety (TLA+).** ✅ the `combined_stacks` race is
49+
reachable in the bug config (concrete 4-state counterexample) and impossible in
50+
the fix (`SignalSafety.tla`); ✅ no deadlock, the handler never blocks on a lock,
51+
output makes progress under fairness (`Deadlock.tla`). ✅ the snapshot algebra
52+
that underlies the fix (`SignalSafety.lean`). ⚠️ TLC is exhaustive only within
53+
bounds (`N=3`, `MaxHandler=2`).
54+
55+
**6. Bounded data structures.** ✅ the Space-Saving `combined_stacks` table never
56+
exceeds capacity and evicts a minimum (`SpaceSaving`), and ✅ this is wired to
57+
production: the proven Lean defs are extracted to Python via
58+
[LeanToPython](https://github.com/emeryberger/LeanToPython) and differentially
59+
tested against the real `_space_saving_increment`
60+
(`tests/test_verified_space_saving.py`).
61+
62+
**Not modeled:** output rendering (the three renderers — guarded by tests, where
63+
the four divide-by-zero bugs below were found), CLI/arg parsing, the
64+
`replacement_*` modules, floating-point rounding (proofs use exact ℚ), and the
65+
Jupyter/AI-provider GUI.
66+
67+
---
3468

3569
Two complementary tools are used, each where it is strongest:
3670

@@ -40,9 +74,17 @@ Two complementary tools are used, each where it is strongest:
4074
| **Lean 4** | [`lean/`](lean/) | conservation/bounds arithmetic, snapshot algebra | machine-checked proof |
4175

4276
> **Why both?** Race/deadlock properties are about *interleavings* — TLC
43-
> exhaustively explores them and produces concrete counterexample traces.
44-
> Conservation/bounds are about *arithmetic over all inputs* — Lean proves them
45-
> for unbounded quantities, which a model checker cannot.
77+
> exhaustively explores them and produces concrete counterexample traces, and it
78+
> checks *liveness* under fairness (progress, no starvation), which Lean has no
79+
> comfortable story for. Conservation/bounds are about *arithmetic over all
80+
> inputs* — Lean proves them for unbounded quantities, which a model checker
81+
> cannot. The engines are complementary, not redundant: retiring the TLC specs
82+
> would drop counterexample-search and liveness coverage with nothing to replace
83+
> them. One overlap is deliberate — `LeakTrackerConcurrency.lean` proves an
84+
> interleaving property in Lean but *assumes step-atomicity as an axiom*
85+
> (justified by the RLock + thread join); deriving that atomicity from the
86+
> sig-queue's operational semantics is a natural next TLA+ job. See
87+
> [`STATUS.md`](STATUS.md) § "Why two engines".
4688
4789
All TLA+ runs and Lean proofs reproduce from a clean checkout (commands below).
4890
The Lean proofs contain **no `sorry`/`admit`** and depend only on Lean's three
@@ -459,6 +501,112 @@ fork reset are in place — both of which are shown here to be required.
459501

460502
---
461503

504+
## 12. PASTA: the sampler→correctness link — `lean/Scalene/PoissonArrivals.lean`
505+
506+
§6 (`ProfilerCorrectness`) *assumes* each timer tick lands on line ℓ with
507+
probability `trueFraction ℓ`. §7 (`ExponentialSampler`) proves the sampler's
508+
inter-arrival gaps are Exponential, so the sample instants form a Poisson
509+
process — but the step "Poisson instants ⇒ landing probability = time fraction"
510+
was cited as PASTA, not proven. This module proves it, in the discrete-time form
511+
the effort targeted.
512+
513+
Model the horizon as `M` equal time slots, `slots i` = the line running during
514+
slot `i`. A Poisson arrival, conditioned on its count, occurs at a uniformly
515+
random time (the order-statistics property) — here, a uniform slot.
516+
517+
- `uniform_landing_eq_timeFraction`**PASTA identity**: the expected indicator
518+
that a uniform arrival lands on ℓ equals ℓ's fraction of time (`timeCount ℓ / M`).
519+
- `sum_timeFraction` — the time fractions form a probability distribution.
520+
- `uniform_realizes_trueFraction`**the discharge**: build the
521+
`ProfilerCorrectness.Truth` induced by the timeline; its `trueFraction` (the
522+
assumed sampling law) equals both the time fraction and
523+
`Truth.expect (indicator ℓ)`. So the hypothesis feeding `estimator_unbiased` /
524+
`jointVariance_eq` is produced by the sampler mechanism, not postulated.
525+
526+
Boundary: this is the discrete-time analogue (uniform-over-slots). The
527+
continuous-time order-statistics theorem for the Poisson process is not
528+
formalized; the discrete form is the operative content for a tick-sampled
529+
profiler.
530+
531+
## 13. Copy volume end-to-end (C++↔Python) — `lean/Scalene/CopyVolumeWiring.lean`
532+
533+
Every other Lean module stops on one side of the native/Python line. This one
534+
spans it, modeling both the C++ `MemcpySampler` accumulator/flush state machine
535+
(`src/include/memcpysampler.hpp:319-361`) and the Python reader
536+
(`process_memcpy_samples`, `scalene_memory_profiler.py:56-99`).
537+
538+
**Source mapping**
539+
540+
| Model element | Scalene source | Meaning |
541+
|---|---|---|
542+
| `cppStep .copy n ℓ` | `incrementMemoryOps`: `_memcpyOps += n` | accumulate bytes, no trigger |
543+
| `cppStep .copyFlush n ℓ` | `sample(n)` triggers → `writeCount()` emits `_memcpyOps`, then `_memcpyOps = 0` | flush accumulator to a record on line ℓ, reset |
544+
| `Record.pid` | `getpid()` in `writeCount` (`snprintf` `%d`) | records tagged with the emitting pid |
545+
| `pythonTotal` filter | `if int(curr_pid) != int(pid): continue` (`:82`) | Python drops foreign-pid records |
546+
| `+= count` | `memcpy_samples[file][line] += count` (`:98`) | Python accumulates per line |
547+
548+
**Theorems**
549+
550+
- `flushed_add_residual` — C++ conservation: bytes written to records + the
551+
unflushed accumulator = total bytes observed. Nothing invented or lost.
552+
- `python_total_eq_flushed` — the mapfile transfer + pid filter neither drop nor
553+
double-count in-process bytes (records carry the running pid, proven via
554+
`cppRun_records_pid`).
555+
- `roundtrip_conservation`**headline**: the copy volume Python reports equals
556+
the bytes C++ observed minus the in-flight residual. `scalene view`'s
557+
copy-volume column faithfully reflects observed memcpy traffic.
558+
- `foreign_pid_dropped` — a child process's records don't pollute this process's
559+
total.
560+
- `residual_zero_after_flush` — a flush resets the accumulator, so the residual
561+
is bounded by one sampling interval; the round-trip discrepancy is at most the
562+
sampling granularity, not arbitrary.
563+
564+
Boundary: models the emitter/reader state machines and the byte accounting, not
565+
the mapfile's low-level byte-format parsing or partial-read handling.
566+
567+
---
568+
569+
## 14. Malloc footprint end-to-end (C++↔Python) — `lean/Scalene/MallocFootprintWiring.lean`
570+
571+
The harder memory path: the *current footprint* / peak-memory number, spanning
572+
the C++ `SampleHeap` emitter (`sampleheap.hpp:183-316`) and the Python reader
573+
`process_malloc_free_samples` (`scalene_memory_profiler.py:102-228`). The C++
574+
half reuses the ThresholdSampler already proven in `MemorySampler.lean`.
575+
576+
**The subtlety, modeled not assumed.** The Python free path clamps the running
577+
footprint to `max(0, current − count)` on every free (`:218`). That clamp
578+
*breaks* pure conservation: if frees drive the footprint below 0 (startup
579+
misses, per the code comment), it silently adds bytes back. A naive model that
580+
ignored the clamp would "prove" conservation falsely. So we prove the honest,
581+
conditional statements:
582+
583+
| Model element | Scalene source | Meaning |
584+
|---|---|---|
585+
| `emitStep`/`emitRun` | `process_malloc`/`process_free` emit on sampler trigger | records carry the reported byte excess, action M/F, pid |
586+
| `stepFootprint` with `max 0 (·)` | `current_footprint = max(0, current − count)` (`:218`) | the free-side clamp, modeled literally |
587+
| `Safe` predicate | "Scalene can miss some initial allocations" (`:215`) | the regime where the clamp is inert |
588+
| `pidFilter` | `if int(curr_pid) != int(pid): continue` (`:145`) | per-process filter |
589+
590+
**Theorems**
591+
592+
- `emit_records_sum` — the records the C++ ThresholdSampler emits sum (signed)
593+
to its `reported` net (bridge to `MemorySampler.threshold_conserves`).
594+
- `clamp_is_identity_of_safe` — while the footprint stays ≥ 0, the clamp is a
595+
no-op and the Python fold is exactly additive.
596+
- `roundtrip_conservation_of_safe`**headline**: in that regime the reported
597+
footprint delta = (true net − sampler residual) / BYTES_PER_MB. End to end.
598+
- `clamp_only_raises`*without* the non-negativity assumption, the clamp can
599+
only push the footprint **up**: the reported footprint is always ≥ the
600+
additive value, so the error is one-sided (over-reporting live memory), never
601+
a silent undercount. The honest unconditional bound.
602+
- `foreign_pid_dropped`, `newline_marker_skipped` — the pid filter and NEWLINE
603+
`continue` faithfully drop records that must not count.
604+
605+
Boundary: reuses the sampler model for the C++ half; models the footprint fold
606+
and clamp, not the mapfile byte-format parsing.
607+
608+
---
609+
462610
## What is *assumed* (model boundary)
463611

464612
These models abstract, and the abstractions are the assumptions:
@@ -473,9 +621,21 @@ These models abstract, and the abstractions are the assumptions:
473621
- **Bounded constants for TLC.** `Keys = {k1,k2,k3}`, `N = 3`, `MaxHandler = 2`.
474622
These bounds make checking exhaustive and finite; the Lean snapshot lemmas
475623
generalize the safety argument to unbounded inputs.
624+
- **PASTA is now proven in discrete-time form** (`PoissonArrivals.lean`): a
625+
uniform arrival over `M` time slots lands on line ℓ with probability equal to
626+
ℓ's time fraction, and this realizes exactly the `trueFraction` sampling law
627+
`ProfilerCorrectness` assumes. The continuous-time order-statistics proof is
628+
still not formalized, but the sampler→correctness link is no longer merely
629+
cited.
630+
- **Copy volume is now modeled end-to-end across the C++/Python boundary**
631+
(`CopyVolumeWiring.lean`): the emitter accumulator/flush state machine and the
632+
Python reader, with round-trip conservation. This is the first metric proven
633+
across the native boundary.
476634
- **Out of scope (not yet modeled):** the C++ allocator's internal thread-local
477-
`_pythonCount`/`_cCount` accounting; the mapfile IPC byte protocol; fork()
478-
lock-state hazards beyond the stop/join discipline; GPU/accelerator paths.
635+
`_pythonCount`/`_cCount` accounting; the mapfile IPC *byte-format* parsing
636+
(the copy-volume model abstracts records, not their on-disk encoding); fork()
637+
lock-state hazards beyond the stop/join discipline; GPU/accelerator device
638+
paths; the per-sample Python/native classifier heuristic accuracy.
479639

480640
---
481641

0 commit comments

Comments
 (0)