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,158 @@
1
+ # Integrating your own data and implementations
2
+
3
+ [Handbook](index.md) · [API](api.md) · [Semantics](semantics.md)
4
+
5
+ Start with the smallest boundary you need. `diff` compares already-materialized
6
+ results without adopting the execution runner. Use an explicit source/adapter or
7
+ compiler only when you need EquiFrame to capture or execute your integration.
8
+ The core does not import the benchmark, a cloud provider or a domain warehouse.
9
+
10
+ ## Reuse a pinned snapshot with core alone
11
+
12
+ `SnapshotClient`, `SnapshotDataSource`, `SnapshotColumn`, `SnapshotTable`,
13
+ `SnapshotManifest` and `snapshot_fingerprint` are available through `equiframe`
14
+ and `equiframe.snapshot`. These core exports are experimental. The existing
15
+ `equiframe_benchmark` and `equiframe_benchmark.snapshot` imports remain exact
16
+ aliases; no migration of historical manifest format 1.0 is required.
17
+
18
+ The base installation can construct/validate manifests without Arrow, a native
19
+ engine or the benchmark. Resolving data needs the `arrow` extra. Executing a
20
+ native transformation additionally needs its corresponding engine extra. No
21
+ new base dependency, remote transport or implicit plugin discovery was added.
22
+
23
+ A client implements two methods:
24
+
25
+ - `get_snapshot(snapshot_id: str) -> SnapshotManifest`
26
+ - `download_artifact(ref: ArtifactRef, destination: Path) -> None`
27
+
28
+ The client owns authentication, transport timeouts and service coordination.
29
+ The source fetches the manifest once at construction, refuses `latest`, pins its
30
+ identity/revisions and optionally selects a declared subset of tables. Each
31
+ resolve downloads to a fresh private run directory and verifies bytes, SHA-256,
32
+ row count, names/order and declared physical/logical types before returning a
33
+ `LoadedTable`. It records the complete manifest identity even for a selection.
34
+ A failed client is not retried silently. The provider must supply genuine
35
+ immutable snapshots; the consumer cannot make a mutable service immutable.
36
+
37
+ The source-distribution example `examples/snapshot_source.py` is self-contained.
38
+ After installing the identified core wheel with the `duckdb` extra and copying
39
+ the example outside the checkout, run:
40
+
41
+ ```bash
42
+ python snapshot_source.py --output fresh-snapshot-example
43
+ ```
44
+
45
+ It uses an injected in-memory client and synthetic Arrow IPC bytes, executes a
46
+ native DuckDB identity transformation, then checks exact Decimal, NULL and
47
+ multiplicity against a literal synthetic table. It needs no benchmark, credentials
48
+ or network service. This is a technical integration example, not acceptance of a
49
+ real service or independent human verification.
50
+
51
+ The snapshot manifest retains its historical ASCII-escaped canonical JSON and
52
+ fingerprint algorithm. Do not substitute contract canonicalization, rename fields,
53
+ normalize strings or rewrite older evidence to make an integration fit.
54
+
55
+ ## A complete external SQLite producer
56
+
57
+ The [SQLite snapshot integration](sqlite-snapshots.md) implements this same port
58
+ outside core with a native owned read transaction, explicit Arrow schemas and
59
+ immutable detached bytes. Its executable examples and tests demonstrate a fixed
60
+ multi-table snapshot during a committed WAL update, exact Decimal/NULL, explicit
61
+ differences and failed byte/schema validation. It is a local reference adapter,
62
+ not a claim of portable isolation semantics across arbitrary DB-API drivers.
63
+
64
+ ## Choose an extension port
65
+
66
+ For CSV/JSON/Parquet, read-only SQLite, API responses, typed Arrow batches and
67
+ composition of multiple explicit providers, see [universal inputs](universal-sources.md).
68
+ External inputs can implement `load(context) -> LoadedTable` and be passed to
69
+ `LocalDataSource`. Existing `DataSource` providers can be bound through
70
+ `CompositeDataSource` without changing core or discovering plugins at import.
71
+
72
+ | Boundary | Implement or register | Responsibility |
73
+ |---|---|---|
74
+ | Captured input | `DataSource` (`table_names`, `resolve`) | Fixed table identity, faithful `LoadedTable`, explicit failure |
75
+ | Artifact storage | `ArtifactStore` (`put`, `get`) | Declared bytes/format and verified hash/size |
76
+ | Native execution | `EngineAdapter` plus `TransformationRegistry` | Your native implementation, declared capabilities and actual output metadata |
77
+ | Bounded relational migration | `PlanCompiler` plus `CompilerRegistry` | Validate/support the declared IR and compile a genuine native transformation |
78
+ | Additional rules | `RuleRegistry` | Evaluate one execution within its declared applicability and budgets |
79
+ | Local publication | `Destination` | Honor verification, idempotency and publication semantics |
80
+
81
+ `PlanCompiler.compile` is a lower-level extension operation: a compiled
82
+ transformation assumes its actual inputs match the validated IR contract.
83
+ `migrate` enforces the physical/value admission before running either engine.
84
+ Calling the compiled transformation through `execute` alone does not reproduce
85
+ that preflight. Native casts can change mismatched input data; `completed` only
86
+ records execution/materialization and establishes no equivalence or correctness.
87
+
88
+ Use explicit instance-owned registries. Registration does not prove support;
89
+ preflight validation and execution still enforce the contract. Do not place
90
+ callback/module names into JSON and expect them to be imported. This library is
91
+ not a sandbox for untrusted extensions.
92
+
93
+ ## Preserve the complete loaded-table boundary
94
+
95
+ A `LoadedTable` can carry an Arrow table, native column types, an index,
96
+ `meaningful_index`, source reference and notes. Preserve those fields when
97
+ wrapping an existing source. Keeping only `.table` or a data-file reference can
98
+ lose the distinction between native and transport types or silently discard an
99
+ index. Meaningful indexes remain unsupported for the current migration IR and
100
+ must be rejected; do not drop one to make migration pass.
101
+
102
+ Generic workflows capture each external source once. Independent stages can
103
+ continue after another source fails under `skip_dependents`; their declared
104
+ resource limits remain enforced separately. A shared-source capture uses the
105
+ permitted consumer envelope, followed by each stage's own limits. Missing or
106
+ invalid inputs never become empty successful tables.
107
+
108
+ ## Bounds you still own
109
+
110
+ Source resolution, callbacks and Arrow decoding can allocate memory before a
111
+ cooperative checkpoint. `RunContext` cancellation, time and byte budgets do not
112
+ preempt a blocked network call or impose a hard process-memory quota. Snapshot
113
+ manifest fetching precedes run budgets. Use bounded clients and an appropriately
114
+ isolated execution service when you need aggregate quotas or hard timeouts.
115
+
116
+ Data-service publication, writer coordination, retention, backups and domain
117
+ mapping remain external responsibilities. The separate SERCOP/DW scripts in the
118
+ repository are owner-maintained prototypes, excluded from the two distributable
119
+ packages. Neither this port nor those scripts establish production readiness,
120
+ accepted real DW integration or support for every Python platform/data type.
121
+
122
+ ## Arrow/Parquet partitions and external-source boundaries
123
+
124
+ The current `DataSource.resolve` and `TableInput.load` ports return an eager
125
+ `LoadedTable`; `ArtifactInput` verifies one local Arrow IPC or Parquet file and
126
+ then materializes its table. A directory of Hive partitions or a remote object
127
+ prefix is not an accepted `ArtifactRef`. Do not present those interfaces as a
128
+ streaming dataset scanner.
129
+
130
+ The existing comparison reader already iterates Arrow record batches and Parquet
131
+ batches under `ComparisonOptions.batch_rows`, and spills comparison partitions
132
+ to private SQLite. New [CSV/JSONL ingestion batches](ingestion.md) emit a normal
133
+ multi-batch Arrow IPC artifact that this reader can consume without a new file
134
+ format. An IPC record batch or Parquet decode may allocate before a checkpoint;
135
+ these are cooperative bounds. `read_table` and execution remain eager.
136
+
137
+ An application can use the existing explicit source port to capture its own
138
+ partitioned dataset, but must pin the complete fragment inventory, versions,
139
+ schemas, multiplicity and order policy, verify every fragment, and return the
140
+ complete declared table. Comparing partitions separately can miss cross-partition
141
+ keys, duplicates or moves. No new partition-directory connector, implicit
142
+ partition-column coercion, concatenation protocol or distributed executor is
143
+ implemented or needed for the bounded local ingestion change.
144
+
145
+ SQLite remains the implemented external-producer reference described above:
146
+ a single owned native read transaction, explicit schemas, detached immutable
147
+ Arrow bytes and the existing `SnapshotClient` contract. Its supported schema,
148
+ size limits and consistency tests are specific to that producer. They do not
149
+ establish PostgreSQL isolation or a generic DB-API connector.
150
+
151
+ No requested source, schema or credentials demonstrate a need for new Excel,
152
+ PostgreSQL or object-storage connectors in this change. Those remain external
153
+ extensions, not installed capabilities. An Excel producer must declare formula
154
+ versus cached values, cell types and textual identifiers; PostgreSQL needs an
155
+ owned consistent multi-query snapshot and exact type mappings; object storage
156
+ needs a fixed inventory/version identity, bounded transport and verified bytes.
157
+ Use existing `DataSource`/`SnapshotClient` ports if such a concrete consumer is
158
+ provided. None of this changes the data-service or domain-warehouse ownership.
@@ -0,0 +1,52 @@
1
+ # Quick local source checks
2
+
3
+ [Handbook](index.md) · [Contributing](contributing.md) · [Performance](performance.md)
4
+
5
+ The quick selection runs locally. Publication is manual; this public repository
6
+ does not configure a GitHub Actions workflow. A quick pass covers the selected
7
+ source checks and does not establish full-suite, installed-package, platform or
8
+ release readiness.
9
+
10
+ ## Run the selection
11
+
12
+ Start at the repository root with a supported interpreter and `uv`. These
13
+ commands select Python 3.12; choose 3.10, 3.11 or 3.13 for another interpreter.
14
+ The runner uses its actual interpreter for strict typing.
15
+
16
+ ```bash
17
+ cd /path/to/equiframe
18
+ uv sync --locked --all-extras --no-editable --python 3.12
19
+ uv run --locked --all-extras python tools/quick_check.py --package core
20
+ cd packages/equiframe-benchmark
21
+ uv sync --locked --all-extras --no-editable --reinstall-package equiframe --python 3.12
22
+ uv run --locked --all-extras python ../../tools/quick_check.py --package benchmark
23
+ cd ../..
24
+ ```
25
+
26
+ Use `--list` to inspect commands without executing them. `--output /new/local/path`
27
+ retains the receipt, logs and pytest files in that new directory. Without it,
28
+ the runner uses a fresh repository `.cache/quick-<id>` directory. Existing output
29
+ directories are rejected. A failed command stops the selection and retains its
30
+ actual exit code; an interrupted or incomplete run is not a pass. Logs stay local
31
+ and may include ordinary diagnostic paths and synthetic fixture values.
32
+
33
+ ## What is checked
34
+
35
+ - Whole-package Ruff lint, formatting, strict mypy and generated-asset drift.
36
+ - The core architecture boundary and an explicit selection of contract, semantic,
37
+ migration, snapshot, compatibility, CLI and product-journey tests.
38
+ - An explicit benchmark selection covering core aliases, grouped splits, training
39
+ selection, evaluation boundaries, the Verify bridge and CLI diagnostics.
40
+
41
+ The exact test lists live in `tools/quick_check.py`. The runner deliberately
42
+ imports the current source directories of the core and benchmark. Its receipt
43
+ records the commit, worktree status, lock digest, interpreter, platform, commands,
44
+ exit codes and elapsed time. Dependency installation is outside those timings.
45
+
46
+ ## Before a release
47
+
48
+ Run the complete checks in [Releasing](releasing.md): full tests, builds, strict
49
+ Twine validation, clean wheel/sdist consumers, exact artifact receipts and the
50
+ affected dependency and platform combinations. Those checks run separately from
51
+ this source-development selection. Local command output does not establish that
52
+ a remote job ran or that a package was published.
@@ -0,0 +1,78 @@
1
+ # First use by role / Primer uso por perfil
2
+
3
+ [Install an identified artifact](installed-private.md) · [Arquitecto](arquitecto.md) ·
4
+ [Científico](cientifico.md) · [API](api.md)
5
+
6
+ For **0.12.4**, use the identified wheel and its reviewed
7
+ dependencies. Run these commands with the Python of that environment, outside
8
+ the source checkout. On Windows, replace `python` with `.venv\Scripts\python.exe`;
9
+ on Linux use `.venv/bin/python`. Activation is optional.
10
+
11
+ ## Español: instalar, comprobar, ejecutar e interpretar
12
+
13
+ 1. Sigue [la instalación offline](installed-private.md), comprueba el SHA-256
14
+ recibido por un canal de confianza y ejecuta `python -m pip check`.
15
+ Para arquitecto instala `[duckdb]`; para científico basta `[arrow]`.
16
+ No necesitas ML, benchmark, OCI, una cuenta nueva ni un servicio adicional.
17
+ 2. Comprueba el intérprete antes de preparar datos:
18
+
19
+ ```text
20
+ python -I -m equiframe doctor --extra duckdb
21
+ python -I -m equiframe doctor --extra arrow --json
22
+ ```
23
+
24
+ `Metadata ready: True` comprueba identidad de versión y disponibilidad local;
25
+ no prueba rangos, DLL, ejecución nativa ni corrección de datos. El JSON conserva
26
+ esa distinción. Una dependencia ausente o metadatos de otra versión devuelven
27
+ **3** y una acción de recuperación. El comando no instala ni usa la red.
28
+ 3. Elige **uno** de estos ejercicios, con una carpeta de salida nueva:
29
+
30
+ ```text
31
+ python -I -m equiframe demo --role architect --output sql-good
32
+ python -I -m equiframe demo --role scientist --output prep-good
33
+ ```
34
+
35
+ Arquitecto: consulta DuckDB local con valores enlazados y esperado literal.
36
+ Científico: IDs con ceros iniciales, Decimal exacto en segundos, NULL y
37
+ timestamps UTC. Las tablas son sintéticas, pequeñas y autocontenidas.
38
+ 4. Comprueba que también detecta problemas:
39
+
40
+ ```text
41
+ python -I -O -m equiframe demo --role scientist --case different --output prep-different
42
+ python -I -O -m equiframe demo --role architect --case not-evaluable --output sql-limit
43
+ ```
44
+
45
+ `equivalent` acepta y devuelve **0**; `different` rechaza y devuelve **1**;
46
+ `not_evaluable` rechaza y devuelve **3**. Los errores de argumentos devuelven
47
+ **2**; fallo de escritura, dependencia o carpeta ya existente devuelve **3**.
48
+ Conserva `$LASTEXITCODE` en PowerShell o `$?` en Linux. No conviertas un rechazo
49
+ esperado del ejercicio en aceptación de datos de tu pipeline.
50
+ 5. Abre el archivo HTML indicado por la salida. `report/report.html` minimiza
51
+ datos; `synthetic-detail/report.html` muestra la clave, columna y valores del
52
+ ejercicio sintético. Se guardan antes del gate. Ambas vistas representan el
53
+ mismo resultado. Las capturas completas permanecen en `private/`: ocultar
54
+ valores en una vista no borra el original. Repetir la misma salida se rechaza;
55
+ elige otra carpeta y conserva la anterior si necesitas comparar intentos.
56
+
57
+ Para sustituir los datos sintéticos por tu pipeline, sigue la guía larga de tu
58
+ perfil y usa `diff` → `write_comparison_report` → `assert_equivalent`. No existe
59
+ una traducción automática de SQL/Python arbitrario ni un servicio desplegado.
60
+ `--help` describe los parámetros reales. Ambos comandos nuevos son experimentales.
61
+
62
+ ## English: a short installed route
63
+
64
+ Install the identified wheel with `[duckdb]` for the architect or `[arrow]`
65
+ for the scientist. Run `python -m pip check`, then `doctor --extra duckdb` or
66
+ `doctor --extra arrow`. A metadata-ready diagnosis is not native validation.
67
+
68
+ Run `demo --role architect --output sql-good` or
69
+ `demo --role scientist --output prep-good` through `python -I -m equiframe`.
70
+ Use `--case different` or `--case not-evaluable` and a **new** output directory to
71
+ inspect rejection. Exit meanings are 0 accepted, 1 different, 2 invalid CLI usage,
72
+ 3 unavailable/failure. `-O` does not disable the gate. Reports are persisted before
73
+ the verdict and never replace an existing destination. Full synthetic detail is
74
+ separate from the default redacted report; raw evidence remains private.
75
+
76
+ Use the role guide for integration with your own Python/SQL and local pytest.
77
+ No automated runtime measurement is presented as a five-minute human observation.
78
+ The independent human-adoption protocol remains [pending](adoption.md).
@@ -0,0 +1,142 @@
1
+ # EquiFrame user handbook
2
+
3
+ [File loading and data conversion](lectura-conversion.md) creates Pandas inputs
4
+ from JSON, Excel, TXT, XML, CSV, Parquet or GeoJSON and offers Polars/DuckDB targets.
5
+ [Optional independent review in R](r_review.md) adds a second exact comparison
6
+ after rules and Isolation Forest without suppressing their alerts.
7
+
8
+ [Experimental standalone EDA](eda.md): raw quality, explicit conversions, grouped
9
+ statistics and investigation candidates in separate private runs. Diagnostics
10
+ never replace full comparison, Verify, model fitting or publication gates.
11
+
12
+
13
+ [Primer uso por perfil / First use](first-use.md)
14
+
15
+ [Recorrido del arquitecto](arquitecto.md) · [Recorrido del científico](cientifico.md)
16
+
17
+ [Inicio en español](inicio.md) · [Choose an API](choosing-api.md) · [Eight journeys](journeys.md)
18
+
19
+ [Lectura JSON, Excel, TXT, XML, CSV, Parquet y GeoJSON con conversión Pandas → Polars/DuckDB](lectura-conversion.md)
20
+
21
+ **Semantic comparison and reviewable evidence for tabular results in Python.**
22
+
23
+ EquiFrame lets you declare what a result must preserve, compare materialized
24
+ outputs, and locate differences in values, types, keys, multiplicity and order.
25
+ You can use it inside an existing pandas, Polars, Arrow or CI workflow without
26
+ adopting its execution runner. Native execution, bounded declarative migration,
27
+ operational verification and anomaly-detection interfaces are separate services.
28
+ Their compatibility commitments do not establish scientific detection accuracy.
29
+
30
+ ## Nuevas fuentes y ventanas
31
+
32
+ El [recorrido de universalidad en cinco pasos](universalidad-cinco-pasos.md)
33
+ ejecuta seis entradas bajo un contrato y tres motores nativos desde el wheel.
34
+ Consulta [fuentes](universal-sources.md), [ventanas](migration-ir.md) y
35
+ [extensiones](extending.md) para sus interfaces y límites.
36
+
37
+ ## Release status
38
+
39
+ This handbook targets **0.12.4**, preserving the **0.12.0 stable release contract**. Preparation and
40
+ publication are separate: the owner has authorized a manual TestPyPI rehearsal
41
+ followed by PyPI, subject to the [release gates](releasing.md). Until registry
42
+ ownership, uploads and clean consumer installation are actually verified, those
43
+ steps remain pending; a source version or a passing local test is not publication.
44
+ Use the exact wheel/sdist SHA-256 and source identity for each identified artifact.
45
+ Historical private Alpha notes retain their original versions and results.
46
+
47
+ Project-owned code uses MIT. Included third-party components retain their
48
+ licenses; the core archive expression is `MIT AND Apache-2.0` because it includes
49
+ CycloneDX schemas. See [licensing](licensing.md) and the packaged notices.
50
+ There is no claimed production certification, scientific accuracy, independent
51
+ human adoption, security-response SLA or accepted real data-warehouse integration.
52
+
53
+ Hernán Troya (`htroya`) is the maintainer and release/security owner, on a
54
+ best-effort basis without a promised backup or response deadline. The public
55
+ [issue channel](https://github.com/htroya/equiframe/issues) and
56
+ [private vulnerability reporting](https://github.com/htroya/equiframe/security/advisories/new)
57
+ are reporting routes of the project's single public repository. For each
58
+ release, the maintainer must enable both routes and verify their availability.
59
+ See [maintenance](maintenance.md) and [security](security.md); never submit
60
+ sensitive details in a public issue.
61
+
62
+ ## Start here
63
+
64
+ 1. [Quick start](quickstart.md): identified artifacts, a clean environment,
65
+ a complete small comparison and a fail-closed assertion
66
+ 2. [Three installed recipes](recipes.md): diagnose a pandas refactor, validate a
67
+ pandas → DuckDB migration and retain evidence before a pytest/CI failure
68
+ 3. [Public API reference](api.md): entry points, models, CLI and safe result states
69
+ 4. [Semantics, guarantees and limits](semantics.md): what equality means, what is
70
+ preserved, what is rejected and what evidence proves
71
+ 5. [Compatibility](compatibility.md): stability, dependency extras, measured
72
+ platforms and the distinction between declared and demonstrated support
73
+ 6. [Performance and resource budgets](performance.md): cost boundaries, bounded
74
+ measurement, missing finalization and practical budget choices
75
+ 7. [Upgrading and troubleshooting](upgrading.md): safe acceptance, historical
76
+ evidence, deprecations and actionable failure diagnosis
77
+ 8. [Extension ports and pinned snapshots](extending.md): reuse your own data client
78
+ without the benchmark, preserve metadata and understand resource ownership
79
+ 9. [Contributing](contributing.md): reproducible checks, review and ownership
80
+ 10. [Dependency inventory](dependencies.md), [security](security.md) and
81
+ [release process](releasing.md): artifact identity, supply-chain evidence,
82
+ disclosure and current release gates
83
+ 11. [Adoption](adoption.md) and [maintenance](maintenance.md): consented real-project
84
+ observations, later-change retention, useful issue reports and critical trust incidents
85
+
86
+ ## Artifact and integration guides
87
+
88
+ - [Normal pip installation from an identified offline wheelhouse](installed-private.md)
89
+ - [Native relational extensions and per-engine limits](migration-ir.md)
90
+ - [CSV/JSONL batches and exact ingestion receipts](ingestion.md)
91
+ - [Installed option inventory and honest coverage denominators](option-audit.md)
92
+
93
+ ## Reading this handbook without the repository
94
+
95
+ The source distribution contains `README.md`, this `docs/` directory, examples,
96
+ tests, tools and JSON schemas. The wheel carries the same handbook as package
97
+ resources under `equiframe/docs/`; it does not require a source checkout.
98
+ After installing the identified wheel, locate it with:
99
+
100
+ ```python
101
+ from importlib.resources import files
102
+
103
+ handbook = files("equiframe").joinpath("docs")
104
+ print(handbook.joinpath("index.md").read_text(encoding="utf-8"))
105
+ ```
106
+
107
+ The first-use kit additionally contains the exact wheel, dependency pins and
108
+ hashes, four recipe/test scripts, an integrity verifier and English guides.
109
+ [Recipes](recipes.md) explains its layout. The package's README is also part of
110
+ its distribution metadata. Relative links within this handbook stay within the
111
+ handbook; no document from another repository is required to follow the core guide.
112
+
113
+ ## Choosing the right service
114
+
115
+ | Need | Entry point | Important boundary |
116
+ |---|---|---|
117
+ | Compare eager tables or an explicitly supplied DuckDB relation | `diff` | Right-hand reference by default; the relation authorizes consumption of its caller-owned native plan, not inference of arbitrary SQL/connections |
118
+ | Compare existing captured outputs or local files | `compare` | Explicit contract; peer comparison by default |
119
+ | Fail a test on anything short of accepted equivalence | `assert_equivalent` | Never use truth testing of the result |
120
+ | Explain a comparison safely | `comparison_report` | Redacted by default; a view is not the acceptance gate |
121
+ | Capture one trusted DuckDB query | `capture_duckdb` | Explicit execution and ownership; detached snapshot |
122
+ | Execute a registered native transformation | `execute` | `completed` means execution, not semantic correctness |
123
+ | Compile and compare a bounded relational plan | `migrate` | Native implementations; not arbitrary program translation |
124
+ | Verify existing outputs against a reviewed reference | `verify` | Agreement, expected-output checks and review remain distinct |
125
+ | Retain transportable finalized evidence | `export_bundle` | Only the supported closed inventories; hashes are not signatures |
126
+ | Study anomalies or engineering costs | Separate `equiframe_benchmark` | Experimental protocol; synthetic checks are not scientific results |
127
+
128
+ The [outcome-gate guide](outcome-gates.md) lists the result/decision models that
129
+ reject implicit truth testing and the explicit field appropriate to each.
130
+
131
+ ## The minimum safe interpretation
132
+
133
+ An `equivalent` result means the **captured outputs satisfy the evaluated
134
+ contract**, including any explicitly authorized tolerance or normalization. It
135
+ does not establish universal program equivalence or business correctness. Two
136
+ engines can share a bug, and a declared expected result can be wrong.
137
+
138
+ `different` and `not_evaluable` both fail an equivalence gate. A complete scan
139
+ with an invalid expected result is still `not_evaluable`. Missing finalization,
140
+ failed publication, resource exhaustion and an unhandled exception are never a
141
+ pass. See [semantics](semantics.md) before using a result to approve a deployment
142
+ or data publication.
@@ -0,0 +1,145 @@
1
+ # Ingest CSV and JSONL in bounded value batches
2
+
3
+ [Handbook](index.md) · [API](api.md) · [Extension ports](extending.md)
4
+
5
+ Use `ingest_file` when converting a local text source under an explicit schema.
6
+ It preserves the original bytes and SHA-256, exact conversion receipts, rejected
7
+ record locations, duplicates and declared Decimal/time precision. It does not
8
+ infer a schema, repair values or establish business correctness.
9
+
10
+ ## A complete small example
11
+
12
+ After installing an identified artifact with the `arrow` extra, run
13
+ this in a fresh directory. The input is deliberately synthetic.
14
+
15
+ ```python
16
+ from decimal import Decimal
17
+ from pathlib import Path
18
+ import equiframe as ef
19
+ import pyarrow as pa
20
+
21
+ path = Path("example.csv")
22
+ path.write_text("id,money\n001,1.10\n001,1.10\n002,NULL\n", encoding="utf-8")
23
+ spec = ef.IngestionSpec(
24
+ format="csv",
25
+ fields=(
26
+ ef.FieldMapping(
27
+ column=ef.ColumnSpec(
28
+ name="id",
29
+ logical_type=ef.StringType(),
30
+ nullable=False,
31
+ null_policy=ef.NullPolicy(representations=("null",)),
32
+ ),
33
+ path=("id",),
34
+ ),
35
+ ef.FieldMapping(
36
+ column=ef.ColumnSpec(
37
+ name="money",
38
+ logical_type=ef.DecimalType(precision=10, scale=2),
39
+ nullable=True,
40
+ null_policy=ef.NullPolicy(representations=("null",)),
41
+ numeric_policy=ef.NumericPolicy(comparison="exact", unit="USD"),
42
+ ),
43
+ path=("money",),
44
+ null_tokens=("NULL",),
45
+ ),
46
+ ),
47
+ )
48
+ result = ef.ingest_file(path, spec=spec, work_dir=Path("ingestion"), batch_rows=2)
49
+ if result.state != "completed" or result.output is None:
50
+ raise RuntimeError("Review the ingestion receipt before accepting these data")
51
+ expected = pa.table(
52
+ {
53
+ "id": ["001", "001", "002"],
54
+ "money": pa.array([Decimal("1.10"), Decimal("1.10"), None], type=pa.decimal128(10, 2)),
55
+ }
56
+ )
57
+ comparison = ef.diff(ef.read_table(result.output), expected, work_dir=Path("comparison"))
58
+ report = ef.write_comparison_report(comparison, Path("report"))
59
+ print(report.to_text())
60
+ ef.assert_equivalent(comparison)
61
+ ```
62
+
63
+ `batch_rows` is an execution knob, default **4096**, accepted by `ingest_file`
64
+ and `FileInput(path, spec, batch_rows=4096)`. It must be a positive Python integer;
65
+ booleans, floats, strings, zero and negative values fail before creating files.
66
+ There is no artificial upper bound: a large batch requests a correspondingly
67
+ large allocation. Record and source budgets still apply. The CLI uses the
68
+ default. The `IngestionSpec`/`IngestionResult` JSON format remains **1.0** and gains
69
+ no field: record the chosen batch size in your application's run configuration.
70
+ Existing calls and historical receipts need no migration.
71
+
72
+ ## What is bounded, and what is not
73
+
74
+ - CSV and JSONL use incremental text decoding and at most one batch of converted
75
+ accepted values before an Arrow IPC write. They no longer retain the full
76
+ decoded file or a list of all JSONL documents
77
+ - An initial bounded decode-validation pass retains the historical behavior for
78
+ invalid encoding: the entire source is rejected before accepting any row.
79
+ The preserved raw file is then read again; this costs an extra sequential pass
80
+ - CSV record positions remain `$[index]`. JSONL keeps its historical `line:N`
81
+ plus nested JSON path; array expansion and root/ancestor scopes are unchanged.
82
+ These are logical record/line positions, not byte offsets. CSV quoted newlines
83
+ retain the prior universal-newline decoding behavior, while original bytes do
84
+ not change
85
+ - Full JSON documents still materialize. A single large JSONL document and its
86
+ explicit nested expansion can also materialize. `max_source_bytes` bounds the
87
+ copied source; `max_records` bounds expanded/inspected records, not parser RSS
88
+ - The public result still retains **all** conversion and quarantine receipt
89
+ objects. Result JSON is now serialized in chunks with exactly the historical
90
+ canonical bytes, avoiding a second complete receipt dictionary/string copy.
91
+ Receipt objects and a single large string/value are not bounded by batch size
92
+ - Context row and Arrow-buffer budgets are cumulative across batches. Arrow
93
+ buffer accounting includes each batch's buffers (including validity bitmaps).
94
+ Physical IPC bytes and hashes can differ with chunk boundaries; semantic
95
+ values, multiplicity, order and receipts must not. These are cooperative
96
+ checks, not hard memory, CPU or disk quotas
97
+ - `FileInput.load`, `read_table` and native execution still return/materialize a
98
+ complete `LoadedTable`. Batching ingestion does not make a whole workflow
99
+ streaming. Existing file-based comparison readers have their own batch/spill
100
+ support; see the [extension boundary](extending.md#arrowparquet-partitions-and-external-source-boundaries)
101
+
102
+ ## CSV headers and surplus cells
103
+
104
+ Any unique, nonempty declared CSV header is legitimate, including names such as
105
+ `<surplus_fields>` or `null`. Diagnostic labels never replace named source cells.
106
+ Rows with additional cells follow `extra_fields`: `ignore` discards only the
107
+ undeclared surplus cells before scalar conversion; `reject` records
108
+ `new_or_extra_field` and follows the declared `invalid_records` policy.
109
+
110
+ Scalar conversion and diagnostics use separate records. For non-colliding names,
111
+ overflow diagnostics retain their historical `original_text` bytes, including
112
+ the existing `null` and `<surplus_fields>` labels. If either label is an actual
113
+ header or declared field path, the diagnostic instead uses the unambiguous
114
+ `{"fields": {named source cells}, "surplus_fields": [extra cells]}` shape inside
115
+ `original_text`. This wrapper is a narrow diagnostic difference for collision
116
+ cases, not a new ingestion schema version; historical 1.0 receipts stay readable.
117
+ No diagnostic labels can become the value of a declared missing field.
118
+
119
+ Original source bytes, SHA-256, source size, logical record positions and
120
+ conversion receipts for named fields are retained. Missing cells still follow
121
+ their declared `missing` policy and never become surplus values.
122
+
123
+ ## Rejection, quarantine and interruption
124
+
125
+ `rejected` has no output reference, even if valid rows had already been flushed
126
+ to private staging. `quarantined` publishes accepted rows but retains the rejected
127
+ records and incomplete coverage. `FileInput.load` accepts only `completed`.
128
+ A successful parse alone does not imply equivalence; use an independent expected
129
+ output or the other requested verification checks.
130
+
131
+ Staged Arrow files are removed on parse rejection, budget exhaustion,
132
+ cancellation, timeout or write failure before publication. Resource and I/O
133
+ errors raise rather than become a successful result. Cancellation or a failure
134
+ while writing the final receipt leaves no `ingestion.json`; its unique operation
135
+ folder may still contain original bytes or already completed artifact files.
136
+ Their existence is not a finalized result. Existing operations are never
137
+ replaced. Original sources and unredacted receipts can contain private values;
138
+ keep evidence in an authorized private directory.
139
+
140
+ The focused tests exercise varying batch sizes, independent exact expected
141
+ values, duplicates, nulls, Unicode, encodings/separators, nested paths, empty
142
+ inputs, cumulative budgets and interruptions. Platform and installed-artifact
143
+ claims require the separate receipt for the exact candidate; source tests and
144
+ historical results are not that receipt. No constant-memory or universal speedup
145
+ claim is made.