equiframe 0.12.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. equiframe/__init__.py +832 -0
  2. equiframe/__main__.py +3 -0
  3. equiframe/_base.py +84 -0
  4. equiframe/_cli_support.py +125 -0
  5. equiframe/_comparison_io.py +161 -0
  6. equiframe/_dataset_provenance.py +146 -0
  7. equiframe/_decimal.py +22 -0
  8. equiframe/_diff_capture.py +433 -0
  9. equiframe/_doctor.py +123 -0
  10. equiframe/_document_io.py +61 -0
  11. equiframe/_documents.py +79 -0
  12. equiframe/_duckdb_transport.py +149 -0
  13. equiframe/_error_reporting.py +52 -0
  14. equiframe/_first_use.py +102 -0
  15. equiframe/_path_compat.py +26 -0
  16. equiframe/_physical.py +133 -0
  17. equiframe/_r_review.R +308 -0
  18. equiframe/_regular_io.py +167 -0
  19. equiframe/_report_format_v1.py +298 -0
  20. equiframe/_report_io.py +591 -0
  21. equiframe/_stability_inventory.py +1421 -0
  22. equiframe/_timezones.py +257 -0
  23. equiframe/_vendor/cyclonedx/LICENSE +201 -0
  24. equiframe/_vendor/cyclonedx/PROVENANCE.md +18 -0
  25. equiframe/_vendor/cyclonedx/bom-1.6.schema.json +5673 -0
  26. equiframe/_vendor/cyclonedx/jsf-0.82.schema.json +240 -0
  27. equiframe/_vendor/cyclonedx/spdx.schema.json +712 -0
  28. equiframe/adapters.py +454 -0
  29. equiframe/assertion_types.py +122 -0
  30. equiframe/assertions.py +271 -0
  31. equiframe/benchmark.py +101 -0
  32. equiframe/bundle.py +240 -0
  33. equiframe/cli.py +693 -0
  34. equiframe/comparison.py +937 -0
  35. equiframe/comparison_report.py +660 -0
  36. equiframe/comparison_types.py +257 -0
  37. equiframe/compatibility.py +546 -0
  38. equiframe/contracts.py +879 -0
  39. equiframe/datasets.py +1301 -0
  40. equiframe/decision.py +34 -0
  41. equiframe/destinations.py +295 -0
  42. equiframe/detector_types.py +431 -0
  43. equiframe/detectors.py +389 -0
  44. equiframe/diff.py +192 -0
  45. equiframe/diff_contracts.py +523 -0
  46. equiframe/docs/adoption.md +329 -0
  47. equiframe/docs/api.md +534 -0
  48. equiframe/docs/arquitecto.md +143 -0
  49. equiframe/docs/choosing-api.md +84 -0
  50. equiframe/docs/cientifico.md +180 -0
  51. equiframe/docs/compatibility.md +254 -0
  52. equiframe/docs/contributing.md +171 -0
  53. equiframe/docs/dependencies.md +237 -0
  54. equiframe/docs/eda.md +166 -0
  55. equiframe/docs/extending.md +158 -0
  56. equiframe/docs/fast-ci.md +52 -0
  57. equiframe/docs/first-use.md +78 -0
  58. equiframe/docs/index.md +142 -0
  59. equiframe/docs/ingestion.md +145 -0
  60. equiframe/docs/inicio.md +129 -0
  61. equiframe/docs/installed-private.md +97 -0
  62. equiframe/docs/journeys.md +362 -0
  63. equiframe/docs/lectura-conversion.md +211 -0
  64. equiframe/docs/licensing.md +17 -0
  65. equiframe/docs/maintenance.md +233 -0
  66. equiframe/docs/migration-ir.md +333 -0
  67. equiframe/docs/option-audit.md +45 -0
  68. equiframe/docs/outcome-gates.md +134 -0
  69. equiframe/docs/performance.md +263 -0
  70. equiframe/docs/piloto-simulado.md +56 -0
  71. equiframe/docs/quickstart.md +156 -0
  72. equiframe/docs/r_review.md +212 -0
  73. equiframe/docs/recipes.md +248 -0
  74. equiframe/docs/releasing.md +183 -0
  75. equiframe/docs/security.md +218 -0
  76. equiframe/docs/semantics.md +274 -0
  77. equiframe/docs/sqlite-snapshots.md +262 -0
  78. equiframe/docs/universal-sources.md +49 -0
  79. equiframe/docs/universalidad-cinco-pasos.md +107 -0
  80. equiframe/docs/upgrading.md +538 -0
  81. equiframe/duckdb_capture.py +410 -0
  82. equiframe/eda/__init__.py +36 -0
  83. equiframe/eda/__main__.py +3 -0
  84. equiframe/eda/_backend.py +612 -0
  85. equiframe/eda/_inventory.py +49 -0
  86. equiframe/eda/_paths.py +49 -0
  87. equiframe/eda/_report.py +291 -0
  88. equiframe/eda/api.py +535 -0
  89. equiframe/eda/cli.py +102 -0
  90. equiframe/eda/models.py +239 -0
  91. equiframe/errors.py +83 -0
  92. equiframe/evidence.py +375 -0
  93. equiframe/examples/integrations/eda_generic.py +60 -0
  94. equiframe/examples/integrations/outcome_gates.py +94 -0
  95. equiframe/examples/integrations/sqlite_snapshot.py +246 -0
  96. equiframe/examples/integrations/sqlite_snapshot_demo.py +226 -0
  97. equiframe/examples/integrations/universal_first_use.py +296 -0
  98. equiframe/examples/journeys/README.md +31 -0
  99. equiframe/examples/journeys/__init__.py +1 -0
  100. equiframe/examples/journeys/a_pandas_refactor.py +88 -0
  101. equiframe/examples/journeys/b_native_migration.py +181 -0
  102. equiframe/examples/journeys/c_retail_etl.py +460 -0
  103. equiframe/examples/journeys/common.py +136 -0
  104. equiframe/examples/journeys/d_financial_reconciliation.py +96 -0
  105. equiframe/examples/journeys/e_telemetry.py +135 -0
  106. equiframe/examples/journeys/f_data_science_preparation.py +84 -0
  107. equiframe/examples/journeys/g_external_snapshots.py +197 -0
  108. equiframe/examples/journeys/h_portable_ci.py +129 -0
  109. equiframe/examples/journeys/requirements.lock +249 -0
  110. equiframe/examples/journeys/verify_journeys.py +117 -0
  111. equiframe/examples/load_convert_compare.py +85 -0
  112. equiframe/execution.py +355 -0
  113. equiframe/execution_types.py +177 -0
  114. equiframe/ingestion.py +744 -0
  115. equiframe/local.py +126 -0
  116. equiframe/migration/__init__.py +94 -0
  117. equiframe/migration/_physical.py +8 -0
  118. equiframe/migration/backends/_timestamp_transport.py +109 -0
  119. equiframe/migration/backends/duckdb.py +651 -0
  120. equiframe/migration/backends/duckdb_decimal.py +125 -0
  121. equiframe/migration/backends/duckdb_time.py +87 -0
  122. equiframe/migration/backends/pandas.py +544 -0
  123. equiframe/migration/backends/polars.py +915 -0
  124. equiframe/migration/backends/polars_decimal.py +231 -0
  125. equiframe/migration/io.py +198 -0
  126. equiframe/migration/ports.py +96 -0
  127. equiframe/migration/runner.py +545 -0
  128. equiframe/migration/runner_types.py +405 -0
  129. equiframe/migration/types.py +354 -0
  130. equiframe/migration/validation.py +619 -0
  131. equiframe/normalization.py +358 -0
  132. equiframe/pipeline.py +274 -0
  133. equiframe/pipeline_types.py +247 -0
  134. equiframe/ports.py +39 -0
  135. equiframe/preprocessing.py +110 -0
  136. equiframe/profile_types.py +343 -0
  137. equiframe/profiles.py +447 -0
  138. equiframe/py.typed +0 -0
  139. equiframe/r_review.py +706 -0
  140. equiframe/reduction.py +518 -0
  141. equiframe/registry.py +121 -0
  142. equiframe/report_rendering.py +366 -0
  143. equiframe/report_types.py +294 -0
  144. equiframe/reporting.py +26 -0
  145. equiframe/rule_types.py +178 -0
  146. equiframe/rules.py +819 -0
  147. equiframe/schemas/r-execution-binding-1.0.schema.json +46 -0
  148. equiframe/schemas/r-review-binding-1.0.schema.json +28 -0
  149. equiframe/schemas/r-review-options-1.0.schema.json +38 -0
  150. equiframe/schemas/r-review-result-1.0.schema.json +207 -0
  151. equiframe/schemas/reviewed-decision-1.0.schema.json +3394 -0
  152. equiframe/serialization.py +152 -0
  153. equiframe/snapshot.py +296 -0
  154. equiframe/sources.py +283 -0
  155. equiframe/tables.py +459 -0
  156. equiframe/types.py +71 -0
  157. equiframe/values.py +252 -0
  158. equiframe/verification_request.py +35 -0
  159. equiframe/verify.py +799 -0
  160. equiframe/verify_types.py +255 -0
  161. equiframe/workflow.py +510 -0
  162. equiframe/workflow_request.py +38 -0
  163. equiframe-0.12.4.dist-info/METADATA +268 -0
  164. equiframe-0.12.4.dist-info/RECORD +170 -0
  165. equiframe-0.12.4.dist-info/WHEEL +4 -0
  166. equiframe-0.12.4.dist-info/entry_points.txt +3 -0
  167. equiframe-0.12.4.dist-info/licenses/LICENSE +21 -0
  168. equiframe-0.12.4.dist-info/licenses/THIRD_PARTY_NOTICES.md +42 -0
  169. equiframe-0.12.4.dist-info/licenses/tools/cyclonedx/LICENSE +201 -0
  170. equiframe-0.12.4.dist-info/licenses/tools/cyclonedx/PROVENANCE.md +18 -0
@@ -0,0 +1,263 @@
1
+ # Performance and safe resource budgets
2
+
3
+ [Handbook](index.md) · [Semantics](semantics.md) · [Compatibility](compatibility.md)
4
+
5
+ EquiFrame favors declared semantics and reviewable evidence over an unconditional
6
+ speed claim. Native assertions may be the smaller service when you only need a
7
+ verdict. There is no universal row limit, latency guarantee, process-RSS quota,
8
+ larger-than-RAM promise or demonstrated advantage over every comparison tool.
9
+
10
+ ## Understand the work you request
11
+
12
+ `diff` first captures native inputs, then compares. `compare` can read local
13
+ captured Arrow IPC/Parquet in batches and uses private SQLite working files for
14
+ exact grouping/alignment. It still validates schema, keys, values, multiplicity
15
+ and all requested evidence. A displayed sample limit does not skip the remaining
16
+ data. Wide strings, many differences and approximate matching can cost more than
17
+ an equal narrow table with the same row count.
18
+
19
+ Common default budgets, as declared by the installed models:
20
+
21
+ | Options | Defaults and scope |
22
+ |---|---|
23
+ | `DiffCaptureOptions` | 1,000,000 rows and 256 MiB per eager input |
24
+ | `ComparisonOptions.batch_rows` | 4,096 rows per comparison batch |
25
+ | `max_batch_bytes`, `max_row_bytes` | 64 MiB per batch; 1 MiB per row |
26
+ | `max_rows`, `max_work` | 10,000,000 rows; 100,000,000 accounted work units |
27
+ | `max_disk_bytes`, `max_evidence_bytes` | 4 GiB comparison working budget; 512 MiB difference evidence |
28
+ | `max_matching_rows`, `max_matching_bytes`, `max_pair_checks` | 2,000 matching rows; 32 MiB; 2,000,000 candidate pair checks |
29
+ | `partitions`, `sqlite_cache_kib` | 16 partitions; 4,096 KiB SQLite cache setting |
30
+ | `max_examples` | 20 summary examples; does not limit evaluation |
31
+ | `timeout_seconds` | None by default; explicit timeouts are cooperative |
32
+ | `DuckDBCaptureOptions` | 4,096-row batches; 1,000,000 rows; 64 MiB batch and 256 MiB file budgets |
33
+
34
+ These are operational checks, not an admission guarantee for every case below
35
+ the numbers. Capture/conversion can allocate before a check. File sizes, Arrow
36
+ buffers, engine-managed memory, working disk and process RSS are distinct.
37
+ `duckdb_memory_mb` constrains engine-managed memory, not all Python/Arrow/native
38
+ allocations. Core callback and Polars collection cancellation are cooperative;
39
+ a callback that does not check can run until it returns. DuckDB query interruption
40
+ does not impose a hard deadline on all Python capture/publication work.
41
+
42
+ Choose a private output directory on a suitable disk-backed filesystem, leave
43
+ space for inputs, temporary files, snapshots and full differences, and avoid
44
+ concurrent benchmark workloads. For large inputs prefer captured files and an
45
+ explicit contract over eager conversion. Set work/time/evidence budgets based on
46
+ a small representative run. Preserve insufficient-budget results instead of
47
+ silently loosening exactness or dropping duplicates.
48
+
49
+ ## Reproducible engineering measurements
50
+
51
+ The separate `equiframe-benchmark` package offers experimental `ScaleSpec`,
52
+ `ScaleTrial`, `ScaleRun`, `measure_scale`, `read_scale` and `summarize_scale`.
53
+ Install its identified matching wheel with the required dependency profile before
54
+ running this complete small example outside the checkout:
55
+
56
+ ```python
57
+ from pathlib import Path
58
+ import equiframe as ef
59
+ import equiframe_benchmark as eb
60
+
61
+ spec = eb.ScaleSpec(
62
+ case_id="keyed-last-batch-1k",
63
+ rows=1_000,
64
+ width=4,
65
+ alignment="keyed",
66
+ null_every=11,
67
+ difference_last=True,
68
+ repetitions=3,
69
+ comparison_options=ef.ComparisonOptions(batch_rows=256),
70
+ concurrent_loads="Record actual known concurrent work; host load is uncontrolled",
71
+ )
72
+ result = eb.measure_scale(spec, work_dir=Path("scale-runs"))
73
+ verified = eb.read_scale(Path("scale-runs") / result.run_id)
74
+ summary = eb.summarize_scale(verified)
75
+ print(summary)
76
+ if not verified.complete:
77
+ raise SystemExit(3)
78
+ ```
79
+
80
+ For meaningful CPU/RSS monitoring install the benchmark's `resources` extra;
81
+ missing or failed monitoring remains explicit. The optional `engines` extra
82
+ permits applicable native baselines. This script measures a deliberately
83
+ **different** case: completion means the expected verdict occurred, not that the
84
+ two tables should pass an equivalence assertion.
85
+
86
+ The generator uses deterministic synthetic integer/text/NULL data with exact keys
87
+ or multisets. Width, string length, cardinality, skew, duplicate multiplicity,
88
+ NULLs, difference density and final-row differences are controlled explicitly.
89
+ It does not establish performance for Decimal, timestamps, approximate matching,
90
+ unknown SQL, remote services, data warehouses or larger-than-RAM datasets.
91
+
92
+ One isolated trusted worker runs 2–10 repetitions sequentially, default 3. The
93
+ first uses a fresh Python worker; subsequent trials reuse it. OS caches are
94
+ uncontrolled. Raw labels distinguish `fresh_process_os_cache_uncontrolled` and
95
+ `warm_process_os_cache_uncontrolled`; summaries retain n/median/min/max, failures
96
+ and missing observations. One fresh observation is not a variability estimate.
97
+
98
+ The recorded stages are capture, snapshot hashing/counting, local transfer,
99
+ contract planning, public comparison, additional standalone serialization,
100
+ persistence, rendering and verification. Public `compare` includes its own
101
+ capture/snapshot/planning/evidence work; the additional serialization/persistence
102
+ stages do not isolate its internals. End-to-end trial time brackets these stages.
103
+ Supervisor elapsed time additionally includes startup, repetitions, baselines,
104
+ monitoring and reaping. Parent final inventory verification/publication is outside
105
+ that measured interval. Do not subtract unlike scopes to invent a component cost.
106
+
107
+ ## What the resource figures mean
108
+
109
+ - RSS is an absolute sampled **lower bound on peak**, not an allocation delta or
110
+ hard maximum. Sample gaps and monitoring failures remain visible
111
+ - Worker CPU/I/O counters are last available observations; physical storage bytes
112
+ and logical syscall bytes are different. Missing final totals stay unknown
113
+ - Disk apparent/allocated peaks are sampled within the owned worker tree and
114
+ temporary directory. Hard links count once; deleted-between-sample or
115
+ open-unlinked files may be missed
116
+ - Engine allocator peak is not supplied by this protocol. Supervisor resources
117
+ and unrelated host work are not included in worker RSS
118
+ - Observed RSS/disk thresholds reactively terminate work and may overshoot; they
119
+ are not cgroups, containers or a sandbox
120
+ - A watchdog can terminate the trusted worker's private process group on POSIX
121
+ or direct child elsewhere. Scheduling delays and unsupported escaping children
122
+ prevent a universal hard-time/containment claim
123
+ - `complete` describes operation/verdict completion;
124
+ `resource_measurement_complete` separately states observation quality
125
+
126
+ Native pandas assertions and DuckDB bidirectional `EXCEPT ALL` baselines are
127
+ restricted to the generator's exact integer/text/NULL policies and original
128
+ Arrow-schema check. They provide less evidence: no complete EquiFrame difference
129
+ files, snapshots, reports and integrity inspection. Ratios are service-cost
130
+ comparisons, not proof one equivalent service is faster. DataComPy is not pinned
131
+ in this candidate and no measurement is invented for it.
132
+
133
+ ## Historical baseline, not a new-artifact speed claim
134
+
135
+ The STEP8 identified Linux x86_64/CPython 3.12.14 artifacts recorded nine synthetic
136
+ cases with three sequential trials each. The narrow 1M-row/two-column last-batch
137
+ case had an all-trial median end-to-end pipeline time of **207.985 seconds**,
138
+ range **206.989–211.278 seconds** and sampled worker peak RSS about **396.8 MiB**.
139
+ The corresponding restricted DuckDB verdict baseline was about **0.493 seconds**
140
+ median, providing substantially less evidence. There is no raw verdict-speed
141
+ advantage for EquiFrame in that example.
142
+
143
+ Those results belong to core wheel SHA-256
144
+ `61030f6755d943954920ef989939fae845bb8b77c69886a347b4fc00e05de1be`
145
+ and benchmark wheel SHA-256
146
+ `e7a227e13c0ea2db926d24e5e6625025bdf4a052e7eac8a0406cb48d6a5a3d96`.
147
+ They are **not measurements of a later rebuilt STEP9 wheel**. All nine runs
148
+ retained `resource_measurement_complete=False` due to incomplete final I/O
149
+ monitoring; some RSS sampling was partial. Host load and OS caches were not
150
+ controlled. These numbers are orientation with explicit limits, not a sizing SLA.
151
+
152
+ The measured narrow 1M recipe used a 1 GiB sampled RSS threshold, 2 GiB sampled
153
+ disk threshold, 1 billion comparison work units, 540 seconds per-comparison
154
+ cooperative timeout and 1,800 seconds supervised allowance for three trials.
155
+ Admission additionally reserves at least disk budget + 256 MiB free space. These
156
+ are fixture-specific budgets, not recommendations for every 1M-row workload.
157
+ The default 100 million work units was insufficient for that fixture.
158
+
159
+ ## Failure, finalization and cleanup
160
+
161
+ `equiframe-benchmark scale spec.json --output scale-runs` and
162
+ `verify-scale scale-runs/scale-ID` return 3 for incomplete/failed measurement or
163
+ invalid finalization. Exit zero by itself is insufficient. The worker's completion
164
+ receipt, finalized aggregate and `finished.json` inventory binding must all
165
+ validate. A native kill, incorrect verdict, corrupted inventory or incomplete
166
+ comparison is not successful measurement.
167
+
168
+ After reaping the worker, only known disposable SQLite files in owned comparison
169
+ directories are removed; partial captures, diagnostics and logs remain. A reader
170
+ does not execute workloads or delete evidence. Receipts retain original local
171
+ paths and are not portable bundles; reproduce from the specification and exact
172
+ artifacts. If the parent dies before publication, `read_scale` fails closed.
173
+
174
+ Tests use bounded subprocesses, genuine SIGKILL and injected ENOSPC rather than
175
+ filling a host disk or causing host OOM. No destructive whole-host stress result
176
+ is claimed. The consumer must treat missing finalization, abnormal exit and
177
+ insufficient evidence as failure or unavailable evaluation, never equivalence.
178
+
179
+ ## Bounded consolidation suite
180
+
181
+ `tools/measure_bounded.py` adds a repeatable developer suite. It reuses
182
+ `ScaleSpec`, `measure_scale`, `read_scale`, `summarize_scale` and their existing
183
+ isolated-worker supervisor for final-row differences, dense differences,
184
+ duplicate multiplicity, wide strings and ordinary disk/evidence costs. A separate
185
+ small Decimal supplement measures exact cents through Arrow and pandas, including
186
+ an explicitly timed Arrow-to-pandas conversion. It does not change historical
187
+ scale schemas or introduce a core dependency on the benchmark.
188
+
189
+ Use a benchmark environment containing the **matching installed** core and
190
+ benchmark builds, including pandas, Arrow and the benchmark `resources` extra.
191
+ From the checkout, after the identified artifacts have been installed:
192
+
193
+ ```bash
194
+ packages/equiframe-benchmark/.venv/bin/python -I \
195
+ tools/measure_bounded.py \
196
+ --output /new/private/bounded-costs --rows 512 --repetitions 3
197
+ ```
198
+
199
+ The scale supervisor deliberately ignores `PYTHONPATH`; pointing the parent at
200
+ changed source while its isolated worker has an older installed wheel fails the
201
+ producer-identity check. Install the matching artifacts instead of weakening the
202
+ check. `suite.json` retains imported-source fingerprints, tool hash, versions,
203
+ individual stage boundaries, raw supervision records and explicit completion.
204
+ An older version string alone is not a matching artifact. Failed attempts remain
205
+ separate; each run requires a new output directory.
206
+
207
+ Defaults run sequentially at 512 rows/side (1,024 for the disk/evidence fixture),
208
+ three repetitions, 128-row batches, a 512 MiB sampled RSS threshold, 64 MiB sampled
209
+ disk threshold and 120 seconds per supervised case. Comparison budgets are 32 MiB
210
+ disk, 8 MiB differences and 30 cooperative seconds. The suite admits only
211
+ 8–2,048 rows and 2–5 repetitions and requires at least 384 MiB initially free.
212
+ These are conservative test boundaries, not hard quotas or workload sizing advice.
213
+ The disk fixture writes complete snapshots and dense differences without trying
214
+ to fill the host disk. The small tests simulate interruption and ENOSPC; they do
215
+ not exhaust memory or storage.
216
+
217
+ The Decimal supplement has its own developer-receipt format rather than claiming
218
+ the historical `ScaleSpec` supports Decimal. It retains the independently expected
219
+ one-cent final-record difference, all comparison records and a redacted report.
220
+ `diff` includes its own conversion/capture, inference, complete comparison and
221
+ publication. Additional conversion, capture, hashing, serialization and durable
222
+ write timings describe additional work; they are not disjoint pieces extracted
223
+ from the internal `diff` timing. A completed comparison followed by failed receipt
224
+ publication is an incomplete measurement. Pre-contract cancellation can raise
225
+ `ExecutionCancelled` before a final comparison exists.
226
+
227
+ ### Measured reference at the consolidation base
228
+
229
+ On 2026-10-06, the installed core and benchmark Python sources were independently
230
+ matched to commit `c4be1c5a29d9f58b2628882bd986de2bb5ca4184`. This **base reference**,
231
+ not a final rebuilt-consolidation measurement, ran all six families with three
232
+ repetitions and complete expected outcomes. Linux x86_64, CPython 3.12.14,
233
+ PyArrow 25.0.1, pandas 3.0.6 and psutil 7.2.2; shared cloud host, uncontrolled OS
234
+ caches and concurrent development work. Each case has one fresh worker trial and
235
+ two warm-worker trials; medians below combine those three explicitly identified
236
+ observations and do not imply cold-cache repeatability.
237
+
238
+ | Case | Rows/side | Pipeline median (range), seconds | Sampled worker RSS, MiB | Sampled apparent disk, MiB |
239
+ |---|---:|---:|---:|---:|
240
+ | Final-row difference | 512 | 0.221 (0.207–0.485) | 147.8 | 1.74 |
241
+ | Every-row difference | 512 | 0.243 (0.228–0.497) | 148.8 | 2.47 |
242
+ | Eight distinct rows, duplicates | 512 | 0.131 (0.130–0.365) | 148.0 | 1.21 |
243
+ | Eight columns, wider strings | 512 | 0.275 (0.271–0.530) | 158.8 | 15.90 |
244
+ | Dense differences plus disk evidence | 1,024 | 0.755 (0.650–0.967) | 160.0 | 24.98 |
245
+ | Exact Decimal final cent | 512 | 0.184 (0.181–0.186) | 149.2 | 1.31 |
246
+
247
+ The public comparison stage dominates these small cases. For example, the
248
+ Decimal median stages were 0.00051 s construction, 0.00087 s additional conversion,
249
+ 0.00027 s additional IPC capture, 0.00007 s hashing, 0.17618 s full `diff`,
250
+ 0.00007 s extra serialization, 0.00005 s extra durable write, 0.00314 s report and
251
+ 0.00146 s readback. Very short stages are below useful stable sizing resolution.
252
+ No algorithm was optimized from these observations and no percentage improvement
253
+ or comparison against a lower-guarantee service is claimed.
254
+
255
+ All six resource receipts remain incomplete because the final I/O sampling
256
+ reported `AccessDenied`. Missing final I/O totals are unknown, not zero. RSS and
257
+ disk columns remain observed lower bounds over the supervised worker and retained
258
+ evidence tree; disk is for the case's three repetitions, not one input file.
259
+ Raw receipts also retain allocated-disk observations and sampling gaps. No new
260
+ macOS/Windows/ARM64, large-volume, storage-saturation or human usability result is
261
+ implied. Preserve the supplied private raw receipts when using this reference;
262
+ source/tool fingerprints and actual environment take precedence over these
263
+ rounded display values.
@@ -0,0 +1,56 @@
1
+ # Tres perfiles ficticios para ensayar el protocolo
2
+
3
+ [Protocolo de adopción](adoption.md) · [Arquitecto](arquitecto.md) · [Científico](cientifico.md)
4
+
5
+ **SIMULADO.** Hernán autorizó preparar perfiles ficticios. No representan personas,
6
+ empresas o proyectos reales; no tienen consentimiento otorgado ni observaciones
7
+ humanas. No se introducen en el registro de pilotos como `human_pilot` y no suman
8
+ al índice U12. No se fabrican firmas, independencia, tiempos o satisfacción.
9
+
10
+ ## Perfil ficticio A: arquitectura e integración SQL
11
+
12
+ - Rol ficticio: arquitecta de datos que integra una comprobación en un orquestador existente
13
+ - Proyecto simulado: comparar una consulta DuckDB de inventario sintético con un
14
+ esperado literal por SKU, conservando clave compuesta y unidades
15
+ - Ruta: guía del arquitecto, captura propia SQL, reporte y aserción; ampliar con
16
+ el recorrido G para fijación de snapshot local
17
+ - Decisiones a ensayar: elegir `[duckdb]`, distinguir snapshot fijado de servicio
18
+ real, localizar B=21 frente a B=20 y detener el siguiente paso
19
+ - Escenario de seguimiento propuesto: una consulta posterior añade un filtro;
20
+ el control debe permanecer. No se afirma que ese cambio haya ocurrido
21
+
22
+ ## Perfil ficticio B: ciencia de datos y preparación
23
+
24
+ - Rol ficticio: científico de datos que conserva identidad y tiempo al preparar features
25
+ - Proyecto simulado: dataset sintético de tres muestras con IDs, Decimal y NULL
26
+ - Ruta: guía del científico; primero `good`, luego desplazamiento temporal,
27
+ pérdida de fila y ausencia reemplazada por cero
28
+ - Decisiones a ensayar: interpretar la diferencia y la consecuencia en ventanas,
29
+ cobertura y joins; mantener el esperado independiente y las ausencias
30
+ - Escenario de seguimiento propuesto: una revisión vectoriza la preparación sin
31
+ cambiar semántica. No hay modelo entrenado ni utilidad científica observada
32
+
33
+ ## Perfil ficticio C: mantenimiento de una regresión CI
34
+
35
+ - Rol ficticio: ingeniera que mantiene pruebas de regresión Python de un proyecto
36
+ - Proyecto simulado: comprobar totales sintéticos después de un refactor,
37
+ conservar el reporte aunque falle pytest y mantener el retorno no cero
38
+ - Ruta: test local de la guía del arquitecto y recorrido A/H
39
+ - Decisiones a ensayar: distinguir cero del arnés que espera fallos del gate que
40
+ acepta datos; usar carpeta nueva y separar una salida completa de equivalencia
41
+ - Escenario de seguimiento propuesto: otro commit cambia el cálculo; el test y
42
+ diagnóstico siguen disponibles. No se ejecuta ni acredita CI remota
43
+
44
+ ## Hoja vacía de ensayo, sin resultados humanos
45
+
46
+ Antes de ensayar, anotar guía/artefactos, propósito y escenario. Si se ejecutan
47
+ comandos, registrarlos como reproducción técnica sintética con sus resultados
48
+ reales. El tiempo automático solo mide comandos; no mide comprensión humana.
49
+ No llenar el consentimiento ni la independencia con valores ficticios positivos.
50
+
51
+ Para pasar de estos perfiles a U12 se requieren personas y proyectos reales,
52
+ consentimiento con alcance/lectores/retención, revisión humana de independencia,
53
+ interpretación y ayuda observadas, tres diagnósticos iniciales <=300 s con reloj
54
+ predeclarado e integración retenida tras un cambio real posterior. Todo ello
55
+ permanece pendiente. El permiso de ensayar perfiles no es permiso de contactar
56
+ personas, compartir datos o publicar resultados.
@@ -0,0 +1,156 @@
1
+ # Quick start
2
+
3
+ [Handbook](index.md) · [Recipes](recipes.md) · [Semantics](semantics.md)
4
+
5
+ This guide targets **0.12.4** artifacts and uses synthetic local data. Its
6
+ commands require identified artifacts after the [release gates](releasing.md)
7
+ pass. Check the official index for availability and verify the artifact hashes.
8
+ It works outside a source checkout. The supported range is Python 3.10–3.13;
9
+ use the exact interpreter/platform dependency closure reviewed for your artifact.
10
+ The first-use builder produces a separate hash lock for the selected minor.
11
+ Metadata admission is not a claim of support for every platform. See
12
+ [compatibility](compatibility.md).
13
+
14
+ ## 1. Install the artifact you were given
15
+
16
+ The commands below demonstrate the optional [offline wheelhouse](installed-private.md)
17
+ route, with normal pip resolution and a separate hash-locked trial. For registry
18
+ installation, use the README after the corresponding [release gates](releasing.md)
19
+ have verified ownership and publication. For an offline delivery, obtain:
20
+
21
+ - `equiframe-0.12.4-py3-none-any.whl`
22
+ - A receipt identifying its commit and expected SHA-256, received through a
23
+ channel you trust independently of the artifact
24
+ - A wheelhouse containing the reviewed runtime dependency wheels for your
25
+ Python/platform and chosen extra, with their pins, hashes and inventory
26
+
27
+ A hash checks bytes against a trusted reference; a manifest packaged with the
28
+ same untrusted archive cannot authenticate its sender. Check the supplied wheel
29
+ against the separate receipt before installing. For example, from its directory:
30
+
31
+ ```bash
32
+ python3.12 -c "from pathlib import Path; import hashlib; p=Path('equiframe-0.12.4-py3-none-any.whl'); print(hashlib.sha256(p.read_bytes()).hexdigest())"
33
+ ```
34
+
35
+ Compare the printed value with the receipt. Stop on a mismatch. Do not copy the
36
+ observed hash back into the expected receipt to make it pass.
37
+
38
+ Create a fresh directory **outside the repository**, then create an environment.
39
+ The following commands use a POSIX shell. Set both paths to your verified local
40
+ locations; `/absolute/path/...` is a placeholder, not a download URL:
41
+
42
+ ```bash
43
+ mkdir equiframe-consumer
44
+ cd equiframe-consumer
45
+ python3.12 -m venv .venv
46
+ . .venv/bin/activate
47
+ WHEEL=/absolute/path/equiframe-0.12.4-py3-none-any.whl
48
+ WHEELHOUSE=/absolute/path/reviewed-wheelhouse
49
+ python -m pip install --no-index --find-links "$WHEELHOUSE" "${WHEEL}[arrow]"
50
+ python -m pip check
51
+ python -I -c "import equiframe, sys; print(sys.version); print(equiframe.__version__); print(equiframe.__file__)"
52
+ ```
53
+
54
+ The import must point into this environment's installed package, not the checkout.
55
+ This command cannot fetch missing dependencies from an index. A missing compatible
56
+ wheel is a blocker to resolve with the artifact provider, not a reason to remove
57
+ integrity checks or install an unrelated `equiframe` from PyPI. For exact hashed
58
+ dependency installation, follow the kit's `--require-hashes` recipe. Do not use an
59
+ editable install or add the checkout to `PYTHONPATH` as an installation test.
60
+
61
+ On Windows, `py -3.12 -m venv .venv` creates the environment and
62
+ `.venv\Scripts\python.exe` can be used directly instead of activation. Do not
63
+ change security policy to activate it. These command conventions do not establish
64
+ that this candidate was verified on Windows. Each Python minor needs its matching
65
+ reviewed dependency set; do not reuse a 3.12 kit lock as a claim of compatibility
66
+ with 3.10, 3.11 or 3.13.
67
+
68
+ ## 2. Compare two complete outputs
69
+
70
+ Save this complete script as `quickstart.py` in the new consumer directory:
71
+
72
+ ```python
73
+ from pathlib import Path
74
+
75
+ import equiframe as ef
76
+ import pyarrow as pa
77
+
78
+ expected = pa.table({"id": ["A", "B"], "quantity": [10, 20]})
79
+ candidate = pa.table({"id": ["B", "A"], "quantity": [21, 10]})
80
+ private_root = Path("private-evidence")
81
+ result = ef.diff(candidate, expected, keys=["id"], work_dir=private_root)
82
+ assert result.state == "different" and result.complete
83
+ assert result.counts.final
84
+
85
+ # Default report hides values, names and paths. It does not delete the originals.
86
+ report = ef.write_comparison_report(result, Path("comparison-report"))
87
+ print(report.to_text())
88
+ print(ef.read_diff_contract(result).reference)
89
+
90
+ try:
91
+ ef.assert_equivalent(result)
92
+ except ef.EquivalenceDifferentError:
93
+ print("The regression was correctly rejected")
94
+ else:
95
+ raise AssertionError("A different result must not pass")
96
+
97
+ fixed = ef.diff(expected, expected, keys=["id"], work_dir=private_root)
98
+ ef.assert_equivalent(fixed)
99
+ print(fixed.state, fixed.complete)
100
+ ```
101
+
102
+ Run it once in that clean directory:
103
+
104
+ ```bash
105
+ python -I quickstart.py
106
+ ```
107
+
108
+ The first comparison is `different`; the corrected one prints `equivalent True`.
109
+ The key aligns `B` with `B` even though row order differs. Its quantity is 21
110
+ instead of 20. The script catches the deliberate failure only to demonstrate the
111
+ API; real tests should let `assert_equivalent` fail.
112
+
113
+ `comparison-report/` contains `report.json`, `report.txt`, `report.html` and a
114
+ publication inventory. Use a new report directory on repetition. `private-evidence/`
115
+ contains original snapshots and differences and can reveal your data. Do not
116
+ upload it automatically. With these known synthetic values only, explicit
117
+ `ReportOptions(include_values=True, include_metadata=True)` makes the diagnosis
118
+ visible. Review disclosure permission before using those options with real data.
119
+
120
+ ## 3. Read states before acting
121
+
122
+ | Comparison state | Meaning | Equivalence gate |
123
+ |---|---|---|
124
+ | `equivalent` | Complete evaluation, no differences under the declared contract | Accepted only if the required guarantee/evidence also passes |
125
+ | `different` | Complete evaluation found a difference or contract violation | Fails |
126
+ | `not_evaluable` | Required judgment cannot be established | Fails, even if `complete=True` |
127
+
128
+ Use `ef.assert_equivalent(result)` or inspect
129
+ `ef.equivalence_decision(result).accepted`. `if result`, `bool(result)` and
130
+ `assert result` raise `TypeError` in every state. A returned object, zero displayed
131
+ examples, a successful HTML export or an integrity reader's exit zero is not an
132
+ equivalence decision.
133
+
134
+ `diff` infers a conservative contract from the right-hand schema, never from a
135
+ search for policies that make the pair agree. Inferred units are `unspecified`,
136
+ numbers are exact, only NULL is a missing-value representation, and nullable is
137
+ a visible default rather than inferred from observed rows. Use reviewed explicit
138
+ `ColumnSpec` definitions for domain units, allowed values and nullability.
139
+
140
+ ## 4. Choose only the dependencies you need
141
+
142
+ | Extra | Purpose |
143
+ |---|---|
144
+ | None | Contract models/validation, serialization, CLI and local artifact support |
145
+ | `arrow` | Tabular comparison and Arrow capture/readers |
146
+ | `pandas`, `polars`, `duckdb` | The named native engine plus PyArrow |
147
+ | `engines` | All three native engines plus PyArrow |
148
+ | `ml` | Fit Isolation Forest with scikit-learn and NumPy |
149
+
150
+ Change `[arrow]` in the local wheel command to the extra you need. Use
151
+ `[engines,ml]` when you need both. Extra names do not supply missing binaries in an
152
+ offline wheelhouse. `import equiframe` itself does not import the optional engines,
153
+ Arrow, NumPy or scikit-learn. Missing optional dependencies fail explicitly.
154
+
155
+ Continue with [the three reproducible recipes](recipes.md),
156
+ [API reference](api.md), and [upgrade/failure guidance](upgrading.md).