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,212 @@
|
|
|
1
|
+
# Revisión independiente con el lenguaje R
|
|
2
|
+
|
|
3
|
+
EquiFrame ofrece `review_with_r`, `finalize_with_r` y
|
|
4
|
+
`finalize_execution_with_r` como una etapa opcional.
|
|
5
|
+
El revisor ejecuta una segunda comparación determinista sobre las dos tablas
|
|
6
|
+
observadas completas y el contrato declarado. Su implementación usa base R;
|
|
7
|
+
no recibe el veredicto del comparador Python ni los scores de los detectores
|
|
8
|
+
como respuesta esperada.
|
|
9
|
+
|
|
10
|
+
Esta independencia de implementación ayuda a encontrar discrepancias entre
|
|
11
|
+
comparadores. No demuestra por sí sola que la migración sea correcta para
|
|
12
|
+
cualquier entrada. Una alerta de Isolation Forest sigue siendo una anomalía,
|
|
13
|
+
no una prueba de diferencia semántica. Replicar el mismo Isolation Forest en
|
|
14
|
+
R no aportaría esa garantía y no forma parte de esta etapa.
|
|
15
|
+
|
|
16
|
+
La letra `r` de `HybridDecision.r` sigue significando **reglas**. El lenguaje R
|
|
17
|
+
no reemplaza reglas, M (Isolation Forest), B (MAD), H (reglas OR M) ni O
|
|
18
|
+
(oráculo científico). El revisor tampoco modifica perfiles, entrenamiento,
|
|
19
|
+
calibración o inferencia de los detectores.
|
|
20
|
+
|
|
21
|
+
## Uso después de reglas e Isolation Forest
|
|
22
|
+
|
|
23
|
+
R debe estar instalado por el usuario y `Rscript` debe estar en `PATH`, o se
|
|
24
|
+
debe indicar su ejecutable. EquiFrame no instala R ni paquetes R. Sólo necesita
|
|
25
|
+
base R. Los ejemplos siguientes describen llamadas de uso; no son ejecuciones
|
|
26
|
+
ni resultados de validación.
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import equiframe as ef
|
|
30
|
+
|
|
31
|
+
# Valores obtenidos previamente de las API reales:
|
|
32
|
+
# execution: ExecutionResult con la salida capturada que se está analizando.
|
|
33
|
+
# rules_result: RuleEvaluation para la ejecución que se está analizando.
|
|
34
|
+
# profile: ProfileResult producido para esa ejecución.
|
|
35
|
+
# detector_bundle: bundle Isolation Forest previamente ajustado y calibrado.
|
|
36
|
+
# python_comparison: ComparisonResult que conserva esa ejecución en uno de sus lados.
|
|
37
|
+
detection = ef.detect_component(detector_bundle, (profile.vector,), method="isolation_forest")
|
|
38
|
+
|
|
39
|
+
final = ef.finalize_execution_with_r(
|
|
40
|
+
execution,
|
|
41
|
+
rules_result,
|
|
42
|
+
profile,
|
|
43
|
+
detection,
|
|
44
|
+
comparison=python_comparison,
|
|
45
|
+
options=ef.RReviewOptions(rscript="Rscript", timeout_seconds=30.0),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Consultar los componentes y su disponibilidad de forma explícita.
|
|
49
|
+
hybrid_decision = final.hybrid.result
|
|
50
|
+
independent_comparison = final.r_review
|
|
51
|
+
previous_comparison = final.comparison
|
|
52
|
+
comparators_disagree = final.comparison_disagreement
|
|
53
|
+
final_decision = final.result
|
|
54
|
+
may_continue = final.accepted
|
|
55
|
+
execution_binding = final.execution_binding
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Ésta es la ruta vinculada: comprueba IDs, huellas de ejecución, contrato,
|
|
59
|
+
artefactos del perfil y el resultado IF de exactamente un vector. Exige que
|
|
60
|
+
la comparación conserve esa ejecución y su salida en uno de sus lados; lee
|
|
61
|
+
directamente los artefactos comprobados de ambos lados. Una asociación inválida
|
|
62
|
+
se rechaza antes de iniciar R, mediante un error de validación. La vinculación
|
|
63
|
+
no autentica al autor humano ni convierte una etiqueta declarada en verdad.
|
|
64
|
+
|
|
65
|
+
`final.execution_binding` es un `RExecutionBinding` con `execution_id`,
|
|
66
|
+
`execution_sha256`, `profile_id`, `profile_sha256`, `detection_sha256` y
|
|
67
|
+
`output_sha256`. Identifica los documentos y la salida realmente asociados por
|
|
68
|
+
el finalizador. El ejemplo no entrena un modelo ni proporciona un bundle ficticio.
|
|
69
|
+
|
|
70
|
+
`finalize_with_r` permanece como API de bajo nivel para quien ya tiene decisiones
|
|
71
|
+
operacionales y observaciones eager. Recibe `rules`, una `ComponentDecision` de
|
|
72
|
+
Isolation Forest, `left`, `right`, `contract` y `comparison` opcional. En esa ruta
|
|
73
|
+
el llamante debe asegurar la asociación de las decisiones; una decisión aislada
|
|
74
|
+
no contiene toda la procedencia de su ejecución. No se declara
|
|
75
|
+
`execution_binding` por deducirla del nombre de una variable.
|
|
76
|
+
|
|
77
|
+
Para solicitar exclusivamente la segunda comparación:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from equiframe.r_review import review_with_r
|
|
81
|
+
|
|
82
|
+
review = review_with_r(
|
|
83
|
+
pandas_output,
|
|
84
|
+
polars_output,
|
|
85
|
+
contract=diff_contract,
|
|
86
|
+
comparison=python_comparison,
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`review_with_r` admite salidas eager de Pandas, Polars, Arrow, `ArrowInput`,
|
|
91
|
+
artefactos locales `ArtifactRef` y `TableArtifact`, y objetos reales
|
|
92
|
+
`DuckDBPyRelation`. Pasar una relación DuckDB autoriza consumir su plan nativo
|
|
93
|
+
una vez por entrada suministrada, con comprobación de tipos y límites de filas
|
|
94
|
+
y bytes. La captura rechaza el exceso de filas, sin truncarlo para aceptar la
|
|
95
|
+
comparación, y conserva una observación separada de la relación. El llamante
|
|
96
|
+
mantiene la propiedad de su conexión, configuración y plan nativo.
|
|
97
|
+
No se infieren cadenas SQL, conexiones, callbacks, lectores consumibles ni
|
|
98
|
+
`LazyFrame` de Polars como entradas tabulares. También puede capturarse DuckDB
|
|
99
|
+
previamente como Arrow/artefacto para revisar una observación ya materializada.
|
|
100
|
+
`comparison` es opcional para la revisión aislada. Cuando se aporta,
|
|
101
|
+
comprueba los SHA-256 de sus artefactos y que todo el transporte observado de
|
|
102
|
+
`left` y `right` corresponda a esos mismos datos antes de enviarlos a R.
|
|
103
|
+
Una especificación distinta bloquea la finalización; el veredicto Python no se
|
|
104
|
+
envía como respuesta esperada.
|
|
105
|
+
|
|
106
|
+
Una revisión vinculada conserva `review.binding`, un `RReviewBinding` con
|
|
107
|
+
`comparison_id`, `left_artifact_sha256` y `right_artifact_sha256`. Una revisión
|
|
108
|
+
aislada sin `comparison` conserva su dictamen, sin inventar ese vínculo.
|
|
109
|
+
|
|
110
|
+
Los modelos de opciones, vínculos y resultados tienen esquemas versionados estables
|
|
111
|
+
`1.0` incluidos en el sdist y como recursos `equiframe/schemas` del wheel:
|
|
112
|
+
`r-review-options`, `r-review-binding`,
|
|
113
|
+
`r-execution-binding`, `r-review-result` y `reviewed-decision`. Su registro
|
|
114
|
+
explícito de compatibilidad conserva la huella del productor actual; no cambia
|
|
115
|
+
los esquemas o recibos anteriores de comparación, reglas y detectores.
|
|
116
|
+
La estabilidad protege la interfaz y los estados de error dentro del subconjunto
|
|
117
|
+
admitido; no acredita precisión científica, todos los contratos posibles ni
|
|
118
|
+
cualquier versión externa de R. Véanse los límites de [compatibilidad](compatibility.md).
|
|
119
|
+
|
|
120
|
+
`contract` acepta `Contract`, `DiffContract` o `ComparisonSpec`; no se inventa
|
|
121
|
+
un esquema o una tolerancia a partir del veredicto anterior. Si se necesita
|
|
122
|
+
inferir un contrato de metadatos, se puede construir antes mediante la API
|
|
123
|
+
existente `infer_diff_contract` y revisar sus políticas.
|
|
124
|
+
|
|
125
|
+
## Resultado final
|
|
126
|
+
|
|
127
|
+
| Condición | Resultado final |
|
|
128
|
+
|---|---|
|
|
129
|
+
| Alguna regla o M alerta | Se conserva `alert`, aunque R encuentre equivalencia. |
|
|
130
|
+
| El comparador Python previo encuentra diferencias | Se conserva `alert`, aunque R encuentre equivalencia. |
|
|
131
|
+
| R encuentra diferencias o violaciones del contrato | `alert`. |
|
|
132
|
+
| H está completo sin alertas, R termina equivalente y la comparación Python aportada también es completa/equivalente con la misma especificación y artefactos vinculados | `no_alert`, `accepted=True`. |
|
|
133
|
+
| Falta R, hay timeout/cancelación/fallo o una política no está admitida | `accepted=False`; se conserva cualquier alerta y se indica evaluación incompleta. |
|
|
134
|
+
| El comparador Python aportado está incompleto o no es evaluable | `accepted=False`; se conserva cualquier alerta y se indica evaluación incompleta. |
|
|
135
|
+
|
|
136
|
+
El campo `final.r_review.state` mantiene por separado `equivalent`,
|
|
137
|
+
`different` o `not_evaluable`. Una comparación equivalente en R y una alerta
|
|
138
|
+
de M no se cancelan: son observaciones distintas. `final.result.complete`
|
|
139
|
+
requiere disponibilidad de todos los componentes solicitados. `no_alert`
|
|
140
|
+
no se convierte en una etiqueta científica de corrección.
|
|
141
|
+
|
|
142
|
+
`comparison` es opcional. Si se aporta, EquiFrame conserva el documento Python
|
|
143
|
+
completo, comprueba las observaciones y lo combina después de ejecutar R, sin
|
|
144
|
+
usar su veredicto como esperado para R.
|
|
145
|
+
`comparison_disagreement=True` indica que ambos comparadores terminaron con
|
|
146
|
+
dictámenes distintos; no decide cuál tiene razón ni borra diferencias.
|
|
147
|
+
Una especificación diferente también se conserva y bloquea la aceptación.
|
|
148
|
+
Si se omite `comparison`, la finalización mantiene su comportamiento H más R;
|
|
149
|
+
no presupone que antes se ejecutó el comparador Python.
|
|
150
|
+
Esta opción corresponde a `finalize_with_r`; la ruta
|
|
151
|
+
`finalize_execution_with_r` exige una comparación vinculada.
|
|
152
|
+
|
|
153
|
+
## Alcance exacto del revisor 1.0
|
|
154
|
+
|
|
155
|
+
Se compara contenido completo, sin muestreo y sin decidir igualdad mediante
|
|
156
|
+
hashes. Se mantienen multiplicidad y claves; puede compararse como multiconjunto
|
|
157
|
+
o como secuencia, comprobando las columnas de orden y sus empates. El orden
|
|
158
|
+
de columnas puede ignorarse sólo cuando el contrato lo autoriza. Un índice
|
|
159
|
+
significativo de Pandas exige la autorización explícita existente para ignorarlo
|
|
160
|
+
o su promoción previa a columnas del contrato.
|
|
161
|
+
|
|
162
|
+
Tipos admitidos: booleanos, cadenas UTF-8, enteros de 8–64 bits con signo o
|
|
163
|
+
sin signo, decimales de precisión física admitida por Arrow, flotantes de
|
|
164
|
+
32/64 bits, fechas `date32` y timestamps. Los enteros y decimales no se
|
|
165
|
+
convierten a `double` en R. Se distingue NULL de NaN; NaN puede ser distinto
|
|
166
|
+
o estar prohibido. Se preservan signos de infinito; cero positivo y negativo
|
|
167
|
+
se comparan según la igualdad numérica exacta existente.
|
|
168
|
+
|
|
169
|
+
Los timestamps zonificados sólo se admiten para comparación de instantes con
|
|
170
|
+
`naive="reject"`; las observaciones ya contienen ticks y zona. Los timestamps
|
|
171
|
+
sin zona se admiten con `naive="preserve"`, sin interpretación local.
|
|
172
|
+
El revisor no resuelve DST ni convierte cadenas de fecha en instantes.
|
|
173
|
+
|
|
174
|
+
Las siguientes configuraciones devuelven `not_evaluable` en esta versión:
|
|
175
|
+
|
|
176
|
+
- Tipos físicos relajados mediante `physical_types="lossless"`.
|
|
177
|
+
- Tolerancias numéricas, redondeo, alias de NULL o NaN convertido a NULL.
|
|
178
|
+
- Restricciones `allowed_values` e intervalos temporales con límites.
|
|
179
|
+
- Resolución de hora local (`assume_zone`), comparación zonificada por hora
|
|
180
|
+
de pared y tipos `date64`.
|
|
181
|
+
- Tipos tabulares anidados o metadatos que la captura fiel actual no admite.
|
|
182
|
+
|
|
183
|
+
Estas limitaciones afectan únicamente a la revisión opcional en R. No
|
|
184
|
+
redefinen ni reducen las capacidades del comparador Python. Una política
|
|
185
|
+
no admitida no se sustituye por otra para obtener una aprobación.
|
|
186
|
+
|
|
187
|
+
## Recursos, privacidad y procedencia
|
|
188
|
+
|
|
189
|
+
Los límites predeterminados son 100.000 filas por entrada, 256 columnas,
|
|
190
|
+
64 MiB de captura por entrada y 64 MiB de transporte acumulado, con 30
|
|
191
|
+
segundos cooperativos para la operación y un proceso R terminable. No son
|
|
192
|
+
cuotas duras de memoria RSS: las copias y los objetos de R tienen sobrecoste.
|
|
193
|
+
La captura de un plan nativo DuckDB precede al proceso R y puede bloquear
|
|
194
|
+
dentro del motor; el tiempo cooperativo no constituye una interrupción dura
|
|
195
|
+
de esa captura.
|
|
196
|
+
|
|
197
|
+
El proceso usa `Rscript --vanilla --default-packages=NULL`, una carpeta
|
|
198
|
+
temporal privada por operación y argumentos estructurados sin shell. Los
|
|
199
|
+
archivos temporales se retiran al terminar, incluso ante fallo o cancelación.
|
|
200
|
+
El resultado devuelve versión de R, estado, conteos y SHA-256 del código,
|
|
201
|
+
especificación y transportes observados. Los hashes sirven para identificar
|
|
202
|
+
entradas; R decide con filas completas. No se incluyen valores de las filas
|
|
203
|
+
en los canales de diagnóstico ni se guarda un modelo adicional.
|
|
204
|
+
|
|
205
|
+
La captura Arrow es un límite compartido con el resto del producto: R vuelve
|
|
206
|
+
a implementar comparación y validaciones admitidas, pero no reinterpreta los
|
|
207
|
+
archivos fuente sin pasar por esa captura. Esta independencia es técnica,
|
|
208
|
+
no una revisión humana ni un oráculo de negocio independiente.
|
|
209
|
+
|
|
210
|
+
Referencias del lanzador y transporte: [Rscript](https://www.stat.ethz.ch/R-manual/R-devel/library/utils/html/Rscript.html),
|
|
211
|
+
[readLines](https://www.stat.math.ethz.ch/R-manual/R-devel/library/base/html/readLines.html)
|
|
212
|
+
y [writeLines](https://www.stat.math.ethz.ch/R-manual/R-devel/library/base/html/writeLines.html).
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Three reproducible installed recipes
|
|
2
|
+
|
|
3
|
+
[Handbook](index.md) · [Quick start](quickstart.md) · [Semantics](semantics.md)
|
|
4
|
+
|
|
5
|
+
A8 introduced the interpreter-aware builder. It supplies offline kits when
|
|
6
|
+
generated for an identified artifact. Retained 0.11.1 first-use results remain
|
|
7
|
+
historical; they do not validate a new release. Also see the offline wheelhouse guide.
|
|
8
|
+
|
|
9
|
+
These recipes describe an identified EquiFrame **0.12.4** first-use kit, when
|
|
10
|
+
provided, and **the Python minor selected in its manifest (3.10-3.13)**.
|
|
11
|
+
First-use kits are generated separately from the source tree. The kit contains
|
|
12
|
+
the exact wheel, three recipe scripts and their
|
|
13
|
+
pytest fixture, pinned dependencies with hashes, an integrity verifier and
|
|
14
|
+
English guides. No checkout, real dataset, credential, remote SQL, notebook or
|
|
15
|
+
service is required. Inputs are small synthetic fixtures defined in the scripts.
|
|
16
|
+
|
|
17
|
+
The target of a first useful diagnosis within five minutes is **not yet validated
|
|
18
|
+
by human observation**. Automated independent artifact checks are technical
|
|
19
|
+
evidence, not a substitute for that observation. Preserve the kit's unfilled
|
|
20
|
+
`OBSERVATION.md` and record a real attempt separately if you participate.
|
|
21
|
+
|
|
22
|
+
## 1. Obtain, verify and install the kit
|
|
23
|
+
|
|
24
|
+
Obtain the candidate kit through the authorized private channel. Compare its
|
|
25
|
+
archive SHA-256 with the separately supplied trusted receipt **before extraction**.
|
|
26
|
+
Extract it into a new directory named `kit`, outside the repository, and open a
|
|
27
|
+
terminal there. Keep the kit unchanged: environments, outputs and completed
|
|
28
|
+
observation records belong in sibling directories. The internal manifest detects
|
|
29
|
+
byte changes; it does not authenticate the sender.
|
|
30
|
+
|
|
31
|
+
The commands below show 3.12. If the manifest selects 3.10, 3.11 or 3.13,
|
|
32
|
+
replace each python3.12 command with that exact interpreter; the supplied
|
|
33
|
+
requirements.lock must belong to that same minor.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python3.12 --version
|
|
37
|
+
python3.12 verify_kit.py
|
|
38
|
+
python3.12 -m venv ../first-use-env
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
On Windows use `py -3.12` in place of `python3.12`. Activate the environment:
|
|
42
|
+
|
|
43
|
+
- Linux/macOS shell: `source ../first-use-env/bin/activate`
|
|
44
|
+
- PowerShell: `..\first-use-env\Scripts\Activate.ps1`. If local policy prevents
|
|
45
|
+
activation, use `..\first-use-env\Scripts\python.exe` directly; do not change
|
|
46
|
+
security policy
|
|
47
|
+
|
|
48
|
+
These are platform command conventions, not new platform acceptance results.
|
|
49
|
+
Install into the new environment:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
python -m pip install --require-hashes --only-binary=:all: -r requirements.lock
|
|
53
|
+
python -m pip install --no-index wheels/equiframe-0.12.4-py3-none-any.whl
|
|
54
|
+
python -m pip check
|
|
55
|
+
python -I -c "import equiframe, sys; print(sys.version); print(equiframe.__file__)"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The **first command deliberately downloads dependencies from your configured pip
|
|
59
|
+
index**; it does not download datasets. Pins and hashes come from the candidate's
|
|
60
|
+
`uv.lock`. The second command installs the verified local EquiFrame wheel with normal
|
|
61
|
+
dependency validation against the already pinned environment.
|
|
62
|
+
The import must resolve to the environment, not the source checkout. Do not add
|
|
63
|
+
`PYTHONPATH` or use an editable install.
|
|
64
|
+
|
|
65
|
+
For offline installation, first obtain a reviewed wheelhouse containing those
|
|
66
|
+
same pinned dependency wheels and add
|
|
67
|
+
`--no-index --find-links /absolute/path/wheelhouse` to the first install command.
|
|
68
|
+
Dependency binaries are not inside the kit. Do not remove hashes or force other
|
|
69
|
+
versions to bypass a failure. If your platform has no compatible wheel, record
|
|
70
|
+
the blocker; this guide does not certify a new platform matrix.
|
|
71
|
+
|
|
72
|
+
A maintainer with the source distribution and built wheel can prepare a kit with
|
|
73
|
+
`python tools/build_first_use_kit.py --python-minor 3.12 --output /absolute/path/new-kit` from the
|
|
74
|
+
core package directory. That builder does not resolve new dependencies or
|
|
75
|
+
publish a package. Consumers continue using only the resulting kit.
|
|
76
|
+
|
|
77
|
+
## 2. Diagnose a pandas refactor
|
|
78
|
+
|
|
79
|
+
Create a sibling working directory and run all commands below from there:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
python -c "from pathlib import Path; Path('../first-use-work').mkdir()"
|
|
83
|
+
cd ../first-use-work
|
|
84
|
+
python -I ../kit/refactor_comparison.py --output refactor-different
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Expected process exit: 1**, a deliberate regression. The report identifies:
|
|
88
|
+
|
|
89
|
+
- `different`, complete evaluation and coverage, strategy `key`
|
|
90
|
+
- Key `id="B"`, column `total_cents`
|
|
91
|
+
- Candidate `integer(2001)` versus expected `integer(2000)`, both `int64`
|
|
92
|
+
- Exact numeric policy, declared unit `USD_cent`, one modified row
|
|
93
|
+
|
|
94
|
+
Open `refactor-different/report/report.html` or read `report.txt`. The inspectable
|
|
95
|
+
contract is `refactor-different/contract.json`. The vectorized refactor added one
|
|
96
|
+
cent. Reordered rows are allowed because the contract ignores order and verifies
|
|
97
|
+
a unique key. The reference preserves the earlier implementation and is also
|
|
98
|
+
checked against three literal fixture values. This does not make pandas or a
|
|
99
|
+
historical output a universal oracle.
|
|
100
|
+
|
|
101
|
+
Run the correction **without changing the policy**:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
python -I ../kit/refactor_comparison.py --case equivalent --output refactor-equivalent
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Expected exit 0, `equivalent`. Try the no-key extension:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
python -I ../kit/refactor_comparison.py --without-key --output refactor-bag
|
|
111
|
+
python -I ../kit/refactor_comparison.py --without-key --case equivalent --output refactor-bag-ok
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Expected exits 1 and 0. Without row identity, EquiFrame compares multisets and
|
|
115
|
+
preserves occurrences; it reports excess rows on each side rather than claiming
|
|
116
|
+
which business row changed. Do not deduplicate to make it pass. This fixture's
|
|
117
|
+
integers are small: it does not promise overflow-free int64 arithmetic for
|
|
118
|
+
arbitrary inputs, fractional currency handling or currency conversion.
|
|
119
|
+
|
|
120
|
+
This script displays only its known synthetic values. `--redacted` hides values
|
|
121
|
+
and metadata in the report. Raw captures are temporary unless you supply
|
|
122
|
+
`--work-dir private-refactor`, which retains private snapshots and full differences.
|
|
123
|
+
Keep that directory separate from reports and do not upload it automatically.
|
|
124
|
+
The attached contract still contains synthetic names/units in redacted mode;
|
|
125
|
+
share only `report/` for the minimized view.
|
|
126
|
+
|
|
127
|
+
## 3. Validate a bounded pandas → DuckDB migration
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
python -I ../kit/migrate_duckdb_recipe.py --case equivalent --output migration-ok
|
|
131
|
+
python -I ../kit/migrate_duckdb_recipe.py --case different --output migration-regression
|
|
132
|
+
python -I ../kit/migrate_duckdb_recipe.py --case not_evaluable --output migration-limited
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Expected exits **0, 1 and 3**. The script uses the real `ef.migrate` API with a
|
|
136
|
+
`Scan` + `Aggregate` relational plan. It does not translate arbitrary Python or
|
|
137
|
+
SQL. Both engines execute their native implementation over the same snapshot;
|
|
138
|
+
the literal expected output is captured separately. Decimal type/scale, currency,
|
|
139
|
+
NULL amounts, empty-sum behavior and counts are declared before execution.
|
|
140
|
+
|
|
141
|
+
The negative case deliberately changes empty-sum semantics. The engines may
|
|
142
|
+
therefore be `equivalent` to each other while `verification="divergent"` against
|
|
143
|
+
the unchanged expected output. This is a shared error, not a fabricated
|
|
144
|
+
cross-engine discrepancy.
|
|
145
|
+
|
|
146
|
+
Each output contains `source-target/`, `source-expected/` and `target-expected/`.
|
|
147
|
+
Each directory retains `report.json`, `report.txt`, `report.html` and
|
|
148
|
+
`evidence-manifest.json`, verified before publication is announced. The limited
|
|
149
|
+
case permits one row per comparison; partial counts are not final totals. Do not
|
|
150
|
+
change the expected output to resolve an evaluation limit.
|
|
151
|
+
|
|
152
|
+
Use `--show-synthetic-values` to inspect the literal expected output and both
|
|
153
|
+
captured results. Expected values are EUR: NULL total/2 rows, JPY: 0.00/1 row and
|
|
154
|
+
USD: exact Decimal 1000000000000000.03/3 rows. The negative case changes only EUR
|
|
155
|
+
to 0.00 in both engines. Each comparison against the expected output retains one
|
|
156
|
+
difference. Values are not printed by default and reports are redacted.
|
|
157
|
+
|
|
158
|
+
`--work-dir private-migration` retains inputs, expected output, native outputs,
|
|
159
|
+
configuration and evidence instead of deleting temporary originals. Never mix
|
|
160
|
+
it with the shareable report directory. The DuckDB adapter owns an in-memory
|
|
161
|
+
connection per execution, uses one thread, an explicit 128 MB engine memory
|
|
162
|
+
limit and UTC session setting, then materializes and closes before returning.
|
|
163
|
+
The script rereads and verifies `read_migration` after that return.
|
|
164
|
+
|
|
165
|
+
The recipe does not open a shared DuckDB file, download extensions or execute
|
|
166
|
+
external SQL. The plan has no temporal values: UTC does not prove unsupported
|
|
167
|
+
date operations. Budgets remain cooperative, not a hard process-RSS quota.
|
|
168
|
+
Passing an actual `DuckDBPyRelation` directly to `diff` authorizes bounded native
|
|
169
|
+
materialization; SQL strings and connections are not accepted as data inputs.
|
|
170
|
+
Explicit capture or an adapter remains available when session ownership and SQL
|
|
171
|
+
execution controls are required. The example does not expand the IR or prove all
|
|
172
|
+
possible inputs valid.
|
|
173
|
+
|
|
174
|
+
## 4. Keep evidence before failing pytest or CI
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
python -I ../kit/assertion_ci.py --case equivalent --output ci-ok
|
|
178
|
+
python -I ../kit/assertion_ci.py --case different --output ci-different
|
|
179
|
+
python -I ../kit/assertion_ci.py --case not_evaluable --output ci-limited
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Expected exits **0, 1 and 3**. The script exports and verifies a report **before**
|
|
183
|
+
calling `ef.assert_equivalent`. Failures retain JSON/text/HTML diagnostics and
|
|
184
|
+
`evidence-manifest.json`. `assert result` and `if result` raise `TypeError` in all
|
|
185
|
+
three comparison states.
|
|
186
|
+
|
|
187
|
+
Run the real pytest fixture with a new output location for each selected case:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
python -I -B -m pytest -q -o addopts= -p no:cacheprovider ../kit/test_assertion_recipe.py -k equivalent
|
|
191
|
+
python -I -B -m pytest -q -o addopts= -p no:cacheprovider ../kit/test_assertion_recipe.py -k different
|
|
192
|
+
python -I -B -m pytest -q -o addopts= -p no:cacheprovider ../kit/test_assertion_recipe.py -k not_evaluable
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Pytest exits **0, 1 and 1** respectively. The final two are deliberately failing
|
|
196
|
+
tests, not `xfail`. Reports are under `ci-reports/<case>/`. Repeat from another
|
|
197
|
+
clean working directory that still points to the kit; never overwrite evidence.
|
|
198
|
+
`-B` prevents bytecode being created alongside the fixture. In your real pipeline,
|
|
199
|
+
provide your actual candidate/reference pair rather than a synthetic case
|
|
200
|
+
selector masquerading as validation.
|
|
201
|
+
|
|
202
|
+
Before uploading a report directory, verify the closed inventory with the
|
|
203
|
+
provided script:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
python -I -c "import runpy; from pathlib import Path; recipe = runpy.run_path('../kit/assertion_ci.py'); recipe['verify_upload_directory'](Path('ci-different'))"
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Run this check even if the test fails (`always()` or your CI equivalent). Upload
|
|
210
|
+
only the four verified report files, and preserve the test's nonzero exit status.
|
|
211
|
+
If publication or manifest verification fails, do not publish a success artifact.
|
|
212
|
+
Do not upload `--work-dir`, environments, snapshots, checkout files or raw JSONL.
|
|
213
|
+
This recipe produces a real local artifact; it does not claim a remote CI job ran.
|
|
214
|
+
|
|
215
|
+
## 5. Change a baseline or contract deliberately
|
|
216
|
+
|
|
217
|
+
There is no automatic "accept all" or reference-update operation. Inspect types,
|
|
218
|
+
values, keys, coverage and policy; fix an erroneous candidate. If a business
|
|
219
|
+
requirement genuinely changed:
|
|
220
|
+
|
|
221
|
+
1. Preserve the failed report, prior reference and their hashes
|
|
222
|
+
2. Record the precise change, rationale, responsible person and review
|
|
223
|
+
3. Create a new contract revision before comparison, changing only the approved
|
|
224
|
+
policy. Generate/review a new expected output independently; never blindly
|
|
225
|
+
copy the failed candidate
|
|
226
|
+
4. Test success, a real difference, an invalid expected result and interruption
|
|
227
|
+
5. Version the approved baseline and contract; review the change in a PR
|
|
228
|
+
|
|
229
|
+
`ef.read_diff_contract(result)` returns the inspectable declaration.
|
|
230
|
+
`DiffContract.model_validate_json(...)` rereads a saved declaration, and
|
|
231
|
+
`ef.diff(candidate, expected, contract=declaration)` reuses it exactly. Neither a
|
|
232
|
+
baseline change nor a new tolerance should be silently learned from a failure.
|
|
233
|
+
The recipes do not modify your external baselines.
|
|
234
|
+
|
|
235
|
+
## Troubleshooting and recording the attempt
|
|
236
|
+
|
|
237
|
+
Use Python 3.12, a clean environment, unchanged pins and fresh directories. Check
|
|
238
|
+
`pip check` and the import location. Exit 1 is expected in deliberate negative
|
|
239
|
+
examples; exit 3 means the requested judgment was not established; exit 2 denotes
|
|
240
|
+
invalid configuration/execution/publication in these scripts. Missing permissions,
|
|
241
|
+
disk space or a finalized report never mean equivalence. Do not publish tracebacks
|
|
242
|
+
containing environment variables or real data.
|
|
243
|
+
|
|
244
|
+
Keep `kit` intact and recheck it with `python ../kit/verify_kit.py`. Save a completed
|
|
245
|
+
copy of `OBSERVATION.md` in the work directory. Record installation time separately,
|
|
246
|
+
include failed attempts and help received, and distinguish engine agreement from
|
|
247
|
+
correctness against a reference. [Semantics](semantics.md) and
|
|
248
|
+
[upgrading](upgrading.md) explain the guarantees and failures in more detail.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Candidate, release, withdrawal and rollback
|
|
2
|
+
|
|
3
|
+
## Current state
|
|
4
|
+
|
|
5
|
+
The release target is `equiframe 0.12.4` with the exactly paired
|
|
6
|
+
`equiframe-benchmark 0.12.4`, identified by commit and artifact SHA-256.
|
|
7
|
+
Source preparation does not establish validation or publication: verify the
|
|
8
|
+
artifact-bound receipts and official index inventory. The public 0.12.2 tag and
|
|
9
|
+
its receipts retain their original identities. The local 0.12.3 candidate tag
|
|
10
|
+
and checks are also preserved; that candidate was not uploaded. The new patch
|
|
11
|
+
must pass its own checks and must not replace previous candidate bytes.
|
|
12
|
+
On 2026-10-27 the owner authorized manual publication to TestPyPI followed by
|
|
13
|
+
PyPI after the release checks pass. Publication is established by the observed
|
|
14
|
+
index inventories and clean downloaded consumers, not by this guide or a build.
|
|
15
|
+
|
|
16
|
+
Hernán Troya (`htroya`) accepted maintenance, release and security responsibility.
|
|
17
|
+
No response-time SLA or staffed on-call coverage is promised. Accounts, 2FA,
|
|
18
|
+
name control, public documentation and private security intake must be verified
|
|
19
|
+
for the selected target. A name lookup does not reserve a package name.
|
|
20
|
+
|
|
21
|
+
Original project code and accompanying materials use MIT. Each distribution
|
|
22
|
+
carries LICENSE and THIRD_PARTY_NOTICES.md. Vendored CycloneDX schemas retain
|
|
23
|
+
Apache-2.0; the core artifact metadata records the combined licensing terms.
|
|
24
|
+
See [license scope](licensing.md) and [offline wheel installation](installed-private.md).
|
|
25
|
+
|
|
26
|
+
The version-2 release policy separates the nine technical checks from factual
|
|
27
|
+
publication gates: security/dependency review, rights, public documentation,
|
|
28
|
+
account protection, independent artifact review and the owner's current task.
|
|
29
|
+
UNKNOWN, missing or failed gates block publication. A temporary packet binds
|
|
30
|
+
observations to the exact commit, four artifacts, destination and date; it does
|
|
31
|
+
not authenticate a human or grant permission by itself. The inspector never
|
|
32
|
+
uploads files or handles credentials. Publication remains a separate manual act.
|
|
33
|
+
|
|
34
|
+
TestPyPI is a public index with separate accounts. Upload only the four approved
|
|
35
|
+
files, download them again, verify every SHA-256 and exercise a clean consumer.
|
|
36
|
+
Promote those same four bytes to PyPI only after this succeeds. Rebuilding or
|
|
37
|
+
changing a file requires new checks. TestPyPI can use an ephemeral local API token
|
|
38
|
+
supplied through a hidden terminal prompt and revoked after upload. Never store
|
|
39
|
+
that token in a repository, a report or GitHub secrets.
|
|
40
|
+
|
|
41
|
+
On 2026-10-28 the owner explicitly selected local manual publication and
|
|
42
|
+
instructed this release not to use GitHub Actions. A newly prepared OIDC
|
|
43
|
+
workflow was withdrawn before use. GitHub jobs are not a prerequisite for this
|
|
44
|
+
manual release route; no environment or publisher configuration is implied.
|
|
45
|
+
|
|
46
|
+
For each index, use its official endpoint and a uniquely named temporary token
|
|
47
|
+
created under the authenticated maintainer account. Pass it through a native
|
|
48
|
+
hidden terminal prompt, upload only the four identified files, then revoke it.
|
|
49
|
+
Keep the exact artifact hashes, account protection and downloaded-consumer
|
|
50
|
+
checks; changing the authentication route does not waive them. Neither account
|
|
51
|
+
passwords nor API tokens belong in source, reports, GitHub secrets or shell
|
|
52
|
+
history.
|
|
53
|
+
|
|
54
|
+
## Tagged versions
|
|
55
|
+
|
|
56
|
+
Hatch and `hatch-vcs` derive both distributions from the same reviewed Git tag
|
|
57
|
+
`vMAJOR.MINOR.PATCH`. Runtime versions come from installed distribution metadata.
|
|
58
|
+
There is no second manually maintained runtime version and no silent fallback
|
|
59
|
+
release number. A source archive retains its package metadata so rebuilding the
|
|
60
|
+
sdist does not require Git history. Release checks reject an absent/mismatched
|
|
61
|
+
tag, dirty or noncanonical source, and SCM version overrides.
|
|
62
|
+
|
|
63
|
+
Create the tag only after the complete source revision is reviewed and committed.
|
|
64
|
+
Use a new patch version for documentation or compatible fixes; use a minor
|
|
65
|
+
version for compatible new functionality and explicitly document breaking
|
|
66
|
+
changes. During the `0.x` series, read the compatibility and upgrade guides.
|
|
67
|
+
Never move an existing release tag or replace published TestPyPI/PyPI files.
|
|
68
|
+
|
|
69
|
+
Historical Alpha candidates keep their original results and closed version-1
|
|
70
|
+
policy. STEP10's real-data evaluation, external scientific acceptance and DW
|
|
71
|
+
integration remain separate research/integration deliverables; they are not
|
|
72
|
+
claimed complete by a library release.
|
|
73
|
+
|
|
74
|
+
## Repeatable candidate checks
|
|
75
|
+
|
|
76
|
+
From the repository root, select the supported Python interpreter for the chosen
|
|
77
|
+
profile. The driver uses each package's exact `uv.lock` and supports the benchmark
|
|
78
|
+
with `--package`:
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
python tools/check_candidate.py \
|
|
82
|
+
--package . --output /absolute/evidence/core-checks.json
|
|
83
|
+
python tools/check_candidate.py \
|
|
84
|
+
--package packages/equiframe-benchmark --output /absolute/evidence/benchmark-checks.json
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Each driver checks locked sync, Ruff check/format, mypy, generated assets, the
|
|
88
|
+
full pytest suite, build, strict Twine validation and installed distribution
|
|
89
|
+
consumption. The final distribution stage uses clean environments outside the
|
|
90
|
+
checkout, exercises optional profiles/recipes, runs shipped tests against
|
|
91
|
+
site-packages and compares both rebuilt wheel and sdist bytes. A passing wheel
|
|
92
|
+
rebuild alone is not a claim about every compiler, platform or dependency build.
|
|
93
|
+
Source snapshots, exact artifact hashes and loaded local dependency bytes bind
|
|
94
|
+
the receipts; they do not authenticate the executor. Keep receipts outside the
|
|
95
|
+
source tree so writing them does not change the source being checked.
|
|
96
|
+
|
|
97
|
+
Candidate checks and manifest generation also require every package and local
|
|
98
|
+
dependency file to match its raw Git HEAD blob. A clean `git status` is not
|
|
99
|
+
enough: automatic line-ending or other Git filter conversions can change the
|
|
100
|
+
bytes that a build packages. On Windows, use a fresh checkout with both
|
|
101
|
+
`core.autocrlf=false` and `core.eol=lf`, for example:
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
git clone --config core.autocrlf=false --config core.eol=lf <repository> <fresh-directory>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Changing these settings in an existing checkout does not rewrite already converted
|
|
108
|
+
files. Rebuild from the canonical checkout; do not normalize an artifact to make
|
|
109
|
+
the verification pass.
|
|
110
|
+
|
|
111
|
+
After freezing a clean commit, run `tools/release_manifest.py` with the exact
|
|
112
|
+
receipts for both packages. Inspect the independent compatibility matrix, the
|
|
113
|
+
installed consumer report, dependency inventory, licenses and dated advisories,
|
|
114
|
+
source review coverage and all unresolved decisions. Build tools and versions
|
|
115
|
+
must be identical for a byte-for-byte reproduction claim.
|
|
116
|
+
|
|
117
|
+
A candidate directory containing both manifests and their adjacent wheel/sdist
|
|
118
|
+
files can be checked with:
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
python tools/verify_publication.py \
|
|
122
|
+
--candidate-root /absolute/candidate --commit EXACT_40_CHARACTER_SHA \
|
|
123
|
+
--target testpypi --gate-packet /absolute/temporary/testpypi-gates.json \
|
|
124
|
+
--output /absolute/evidence/publication-readiness.json
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Malformed, stale, incomplete, linked or substituted artifacts are rejected.
|
|
128
|
+
For the current version-2 policy, exit **0** means that the technical receipts
|
|
129
|
+
and all factual gates for the selected index passed; missing, UNKNOWN or failed
|
|
130
|
+
gates block readiness with a nonzero exit. Use `--target pypi` and a separate
|
|
131
|
+
packet after verifying the TestPyPI downloads and consumer installation.
|
|
132
|
+
Historical version-1 candidates retain their closed policy and exit **3**.
|
|
133
|
+
There is no `--force` or credential switch. The packet does not authenticate
|
|
134
|
+
human approval, and this command never uploads or grants account access.
|
|
135
|
+
|
|
136
|
+
## Local checks and provenance
|
|
137
|
+
|
|
138
|
+
The public repository ships no GitHub Actions workflows. Run the local candidate
|
|
139
|
+
commands above and retain their exact source identities, environment details,
|
|
140
|
+
exit codes and artifact hashes. A local receipt establishes only the work it
|
|
141
|
+
records; it does not establish that a remote CI job ran. Dependency advisories
|
|
142
|
+
are separate networked checks and retain unknown or unavailable outcomes.
|
|
143
|
+
|
|
144
|
+
Publication is manual with a local ephemeral credential. A future adoption of
|
|
145
|
+
Trusted Publishing requires a separately reviewed protected workflow and real
|
|
146
|
+
publisher configuration; OIDC is not configured or used for this release.
|
|
147
|
+
|
|
148
|
+
## Correcting a defect
|
|
149
|
+
|
|
150
|
+
1. Record exact affected commits, wheel hashes, APIs/adapters, policies and
|
|
151
|
+
environments. Preserve a sanitized reproducer and independent expected result
|
|
152
|
+
2. Assess exposure and whether the claim of equivalence, privacy or evidence
|
|
153
|
+
integrity is affected. Stop recommending the affected route if the judgment
|
|
154
|
+
can be wrong; fail closed rather than silently weakening the contract
|
|
155
|
+
3. Assign an owner before promising a date. Implement a regression test and
|
|
156
|
+
narrow correction. Re-run the relevant historic corpus, full gates, installed
|
|
157
|
+
recipes and affected compatibility cells on new immutable artifacts
|
|
158
|
+
4. Refresh SBOM/advisory/license evidence and publish release notes describing
|
|
159
|
+
behavior changes, affected versions, migration and remaining limitations
|
|
160
|
+
5. Obtain the required release approval and distribute a new version. Public
|
|
161
|
+
artifacts are immutable: do not overwrite a released version with new bytes
|
|
162
|
+
|
|
163
|
+
## Withdrawal and rollback
|
|
164
|
+
|
|
165
|
+
For a private candidate, stop distributing the affected hash and notify only
|
|
166
|
+
its authorized recipients through verified channels. Keep the incident record
|
|
167
|
+
and superseded evidence so results can be traced. For a future public release,
|
|
168
|
+
the assigned owner must approve the appropriate advisory and index action;
|
|
169
|
+
yanking is a selection signal, not deletion of all installed copies or pinned
|
|
170
|
+
references. Deletion is a separate, potentially irreversible action.
|
|
171
|
+
|
|
172
|
+
Test rollback in a **new environment** using the previous known-good exact
|
|
173
|
+
artifacts and their matching pinned dependencies. Verify hashes, imports,
|
|
174
|
+
`pip check`, recipes and historic evidence readers before replacing a consumer
|
|
175
|
+
service. Rollback must not rewrite user evidence or downgrade a versioned file
|
|
176
|
+
format silently. If a new format is unreadable by the old version, retain the
|
|
177
|
+
new reader in an isolated environment or use an explicitly reviewed migration.
|
|
178
|
+
Never infer compatibility from equal version numbers on private candidates.
|
|
179
|
+
Re-running old tests with a new hash is a new receipt, not a historic result.
|
|
180
|
+
|
|
181
|
+
Official background: [Python packaging](https://packaging.python.org/en/latest/tutorials/packaging-projects/),
|
|
182
|
+
[PyPI attestations](https://docs.pypi.org/attestations/),
|
|
183
|
+
[PyPI yanking](https://docs.pypi.org/project-management/yanking/)
|