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.
- equiframe/__init__.py +832 -0
- equiframe/__main__.py +3 -0
- equiframe/_base.py +84 -0
- equiframe/_cli_support.py +125 -0
- equiframe/_comparison_io.py +161 -0
- equiframe/_dataset_provenance.py +146 -0
- equiframe/_decimal.py +22 -0
- equiframe/_diff_capture.py +433 -0
- equiframe/_doctor.py +123 -0
- equiframe/_document_io.py +61 -0
- equiframe/_documents.py +79 -0
- equiframe/_duckdb_transport.py +149 -0
- equiframe/_error_reporting.py +52 -0
- equiframe/_first_use.py +102 -0
- equiframe/_path_compat.py +26 -0
- equiframe/_physical.py +133 -0
- equiframe/_r_review.R +308 -0
- equiframe/_regular_io.py +167 -0
- equiframe/_report_format_v1.py +298 -0
- equiframe/_report_io.py +591 -0
- equiframe/_stability_inventory.py +1421 -0
- equiframe/_timezones.py +257 -0
- equiframe/_vendor/cyclonedx/LICENSE +201 -0
- equiframe/_vendor/cyclonedx/PROVENANCE.md +18 -0
- equiframe/_vendor/cyclonedx/bom-1.6.schema.json +5673 -0
- equiframe/_vendor/cyclonedx/jsf-0.82.schema.json +240 -0
- equiframe/_vendor/cyclonedx/spdx.schema.json +712 -0
- equiframe/adapters.py +454 -0
- equiframe/assertion_types.py +122 -0
- equiframe/assertions.py +271 -0
- equiframe/benchmark.py +101 -0
- equiframe/bundle.py +240 -0
- equiframe/cli.py +693 -0
- equiframe/comparison.py +937 -0
- equiframe/comparison_report.py +660 -0
- equiframe/comparison_types.py +257 -0
- equiframe/compatibility.py +546 -0
- equiframe/contracts.py +879 -0
- equiframe/datasets.py +1301 -0
- equiframe/decision.py +34 -0
- equiframe/destinations.py +295 -0
- equiframe/detector_types.py +431 -0
- equiframe/detectors.py +389 -0
- equiframe/diff.py +192 -0
- equiframe/diff_contracts.py +523 -0
- equiframe/docs/adoption.md +329 -0
- equiframe/docs/api.md +534 -0
- equiframe/docs/arquitecto.md +143 -0
- equiframe/docs/choosing-api.md +84 -0
- equiframe/docs/cientifico.md +180 -0
- equiframe/docs/compatibility.md +254 -0
- equiframe/docs/contributing.md +171 -0
- equiframe/docs/dependencies.md +237 -0
- equiframe/docs/eda.md +166 -0
- equiframe/docs/extending.md +158 -0
- equiframe/docs/fast-ci.md +52 -0
- equiframe/docs/first-use.md +78 -0
- equiframe/docs/index.md +142 -0
- equiframe/docs/ingestion.md +145 -0
- equiframe/docs/inicio.md +129 -0
- equiframe/docs/installed-private.md +97 -0
- equiframe/docs/journeys.md +362 -0
- equiframe/docs/lectura-conversion.md +211 -0
- equiframe/docs/licensing.md +17 -0
- equiframe/docs/maintenance.md +233 -0
- equiframe/docs/migration-ir.md +333 -0
- equiframe/docs/option-audit.md +45 -0
- equiframe/docs/outcome-gates.md +134 -0
- equiframe/docs/performance.md +263 -0
- equiframe/docs/piloto-simulado.md +56 -0
- equiframe/docs/quickstart.md +156 -0
- equiframe/docs/r_review.md +212 -0
- equiframe/docs/recipes.md +248 -0
- equiframe/docs/releasing.md +183 -0
- equiframe/docs/security.md +218 -0
- equiframe/docs/semantics.md +274 -0
- equiframe/docs/sqlite-snapshots.md +262 -0
- equiframe/docs/universal-sources.md +49 -0
- equiframe/docs/universalidad-cinco-pasos.md +107 -0
- equiframe/docs/upgrading.md +538 -0
- equiframe/duckdb_capture.py +410 -0
- equiframe/eda/__init__.py +36 -0
- equiframe/eda/__main__.py +3 -0
- equiframe/eda/_backend.py +612 -0
- equiframe/eda/_inventory.py +49 -0
- equiframe/eda/_paths.py +49 -0
- equiframe/eda/_report.py +291 -0
- equiframe/eda/api.py +535 -0
- equiframe/eda/cli.py +102 -0
- equiframe/eda/models.py +239 -0
- equiframe/errors.py +83 -0
- equiframe/evidence.py +375 -0
- equiframe/examples/integrations/eda_generic.py +60 -0
- equiframe/examples/integrations/outcome_gates.py +94 -0
- equiframe/examples/integrations/sqlite_snapshot.py +246 -0
- equiframe/examples/integrations/sqlite_snapshot_demo.py +226 -0
- equiframe/examples/integrations/universal_first_use.py +296 -0
- equiframe/examples/journeys/README.md +31 -0
- equiframe/examples/journeys/__init__.py +1 -0
- equiframe/examples/journeys/a_pandas_refactor.py +88 -0
- equiframe/examples/journeys/b_native_migration.py +181 -0
- equiframe/examples/journeys/c_retail_etl.py +460 -0
- equiframe/examples/journeys/common.py +136 -0
- equiframe/examples/journeys/d_financial_reconciliation.py +96 -0
- equiframe/examples/journeys/e_telemetry.py +135 -0
- equiframe/examples/journeys/f_data_science_preparation.py +84 -0
- equiframe/examples/journeys/g_external_snapshots.py +197 -0
- equiframe/examples/journeys/h_portable_ci.py +129 -0
- equiframe/examples/journeys/requirements.lock +249 -0
- equiframe/examples/journeys/verify_journeys.py +117 -0
- equiframe/examples/load_convert_compare.py +85 -0
- equiframe/execution.py +355 -0
- equiframe/execution_types.py +177 -0
- equiframe/ingestion.py +744 -0
- equiframe/local.py +126 -0
- equiframe/migration/__init__.py +94 -0
- equiframe/migration/_physical.py +8 -0
- equiframe/migration/backends/_timestamp_transport.py +109 -0
- equiframe/migration/backends/duckdb.py +651 -0
- equiframe/migration/backends/duckdb_decimal.py +125 -0
- equiframe/migration/backends/duckdb_time.py +87 -0
- equiframe/migration/backends/pandas.py +544 -0
- equiframe/migration/backends/polars.py +915 -0
- equiframe/migration/backends/polars_decimal.py +231 -0
- equiframe/migration/io.py +198 -0
- equiframe/migration/ports.py +96 -0
- equiframe/migration/runner.py +545 -0
- equiframe/migration/runner_types.py +405 -0
- equiframe/migration/types.py +354 -0
- equiframe/migration/validation.py +619 -0
- equiframe/normalization.py +358 -0
- equiframe/pipeline.py +274 -0
- equiframe/pipeline_types.py +247 -0
- equiframe/ports.py +39 -0
- equiframe/preprocessing.py +110 -0
- equiframe/profile_types.py +343 -0
- equiframe/profiles.py +447 -0
- equiframe/py.typed +0 -0
- equiframe/r_review.py +706 -0
- equiframe/reduction.py +518 -0
- equiframe/registry.py +121 -0
- equiframe/report_rendering.py +366 -0
- equiframe/report_types.py +294 -0
- equiframe/reporting.py +26 -0
- equiframe/rule_types.py +178 -0
- equiframe/rules.py +819 -0
- equiframe/schemas/r-execution-binding-1.0.schema.json +46 -0
- equiframe/schemas/r-review-binding-1.0.schema.json +28 -0
- equiframe/schemas/r-review-options-1.0.schema.json +38 -0
- equiframe/schemas/r-review-result-1.0.schema.json +207 -0
- equiframe/schemas/reviewed-decision-1.0.schema.json +3394 -0
- equiframe/serialization.py +152 -0
- equiframe/snapshot.py +296 -0
- equiframe/sources.py +283 -0
- equiframe/tables.py +459 -0
- equiframe/types.py +71 -0
- equiframe/values.py +252 -0
- equiframe/verification_request.py +35 -0
- equiframe/verify.py +799 -0
- equiframe/verify_types.py +255 -0
- equiframe/workflow.py +510 -0
- equiframe/workflow_request.py +38 -0
- equiframe-0.12.4.dist-info/METADATA +268 -0
- equiframe-0.12.4.dist-info/RECORD +170 -0
- equiframe-0.12.4.dist-info/WHEEL +4 -0
- equiframe-0.12.4.dist-info/entry_points.txt +3 -0
- equiframe-0.12.4.dist-info/licenses/LICENSE +21 -0
- equiframe-0.12.4.dist-info/licenses/THIRD_PARTY_NOTICES.md +42 -0
- equiframe-0.12.4.dist-info/licenses/tools/cyclonedx/LICENSE +201 -0
- 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).
|
equiframe/docs/index.md
ADDED
|
@@ -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.
|