Skip to content

Commit 8d55573

Browse files
committed
Merge remote-tracking branch 'origin/main' into agent/fix-strict-sav-export
# Conflicts: # CHANGELOG.md # src/openstatspec/sql/inplace_transform.py # src/openstatspec/sql/wide.py # tests/test_atomic_import.py # tests/test_attribute_catalog.py # tests/test_loss_reports.py # tests/test_sql_services.py # tests/test_vls_compatible_names.py
2 parents 95b0506 + cdd7680 commit 8d55573

44 files changed

Lines changed: 5909 additions & 677 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
uses: actions/checkout@v7
2121
with: &specification-checkout
2222
repository: OpenStatSpec/specification
23-
ref: 79339ec3d8f8aa81789b7e85f6b8afa6f1374e50
23+
ref: f2fdf687d8cb32b944ca55a3e9e7215ffc603019
2424
path: openstatspec-specification
2525
- name: Checkout required SPSS engine
2626
uses: actions/checkout@v7
@@ -143,7 +143,7 @@ jobs:
143143
steps: *sql-test-steps
144144

145145
dolt-integration:
146-
name: Dolt ${{ matrix.dolt }} integration
146+
name: Dolt ${{ matrix.dolt }} read-only / fail-closed integration
147147
runs-on: ubuntu-latest
148148
strategy:
149149
fail-fast: false
@@ -217,7 +217,7 @@ jobs:
217217
connection.close()
218218
break
219219
PY
220-
- run: python -m pytest -m services
220+
- run: python -m pytest -m "services and not candidate_evidence"
221221
- name: Show Dolt logs on failure and stop server
222222
if: always()
223223
run: |

‎.github/workflows/release.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
uses: actions/checkout@v7
4848
with:
4949
repository: OpenStatSpec/specification
50-
ref: 79339ec3d8f8aa81789b7e85f6b8afa6f1374e50
50+
ref: f2fdf687d8cb32b944ca55a3e9e7215ffc603019
5151
path: openstatspec-specification
5252
- name: Checkout required SPSS engine
5353
uses: actions/checkout@v7

‎CHANGELOG.md‎

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,53 @@ All notable changes to this reference implementation are documented here.
44

55
## Unreleased
66

7+
Planned adapter release: `0.5.0`, after lifecycle integration and final specification conformance.
78
### Fixed
89

910
- Removed the temporary `*_catalog` compatibility schema and made SAV/ZSAV
1011
import, validation, export, fidelity reporting, and in-place metadata edits
1112
use the normative UUID-keyed OpenStatSpec catalog exclusively.
12-
- Existing databases that contain only the former compatibility catalog must be
13-
remediated manually before export.
13+
- Existing databases that contain former compatibility relations must be
14+
remediated manually before further operations.
1415
- Imports now reject dataset names that collide with normative UUID identifiers,
1516
cleanup drops only a physical table actually created by the failing import,
1617
and non-preserved SPSS compatible variable names are reported as an explicit
1718
loss requiring export consent.
1819

20+
21+
### Added
22+
23+
- Added bounded typed expressions with variable references, numeric literals,
24+
parentheses, `=`, `<`, `<=`, `>`, `>=`, `AND`, and `OR`. String comparison
25+
and v0.2 string assignment fail closed pending exact portable semantics.
26+
- Added sequential SPSS-like `COMPUTE` and `IF` assignment operations plus
27+
`FORMATS`, `VARIABLE LEVEL`, and `EXECUTE`.
28+
- Added atomic numeric target creation on SQLite and PostgreSQL. MySQL,
29+
MariaDB, and Dolt fail closed on `target_mode=create`; their targets must be
30+
provisioned physically and in the catalog by a separate versioned stage
31+
before this executor applies assignment and metadata operations.
32+
- Added synthetic exact-program, catalog, failure-boundary, and pre-existing
33+
target regression coverage.
34+
35+
### Changed
36+
37+
- Bumped the canonical transformation-plan and SPSS frontend contracts to
38+
`v0.2`; canonical JSON and hashes include every sequential operation.
39+
- In-place apply records variable label, value labels, `F` print/write
40+
format, and measurement level only in the normative catalog.
41+
- Dolt still requires an exact branch, exact HEAD, and clean working set;
42+
successful apply leaves an inspectable diff and never calls `DOLT_COMMIT`.
43+
- Dolt declaration validation is implemented by the Python adapter. The
44+
specification remains a language-neutral source of JSON contracts and
45+
fixtures and is not installed as a Python distribution.
46+
47+
### Specification basis
48+
49+
- Release validation is pinned to the untagged OpenStatSpec specification
50+
release candidate at immutable commit
51+
`f2fdf687d8cb32b944ca55a3e9e7215ffc603019`. Its
52+
`specification_release` remains null until that commit receives a stable tag.
53+
1954
## 0.4.0 — 2026-07-31
2055

2156
### Added
@@ -123,6 +158,17 @@ SPSS profile.
123158
- Export of supported dataset semantics to SAV and ZSAV.
124159
- SQLite, PostgreSQL, MySQL, and MariaDB profiles, including service-backed
125160
PostgreSQL 17/18, MySQL 8.4/9.7, and MariaDB 11.4/11.8/12.3 CI coverage.
161+
- Read-only positive Dolt identity and working-set inspection. Operational
162+
Dolt writes use adapter-owned declaration validation and remain fail-closed
163+
because the adapter ships no concrete declaration claim. The specification
164+
remains language-neutral and is consumed only as JSON contracts and fixtures.
165+
- Added explicit packaged/directory conformance-source injection and exact
166+
single-match binding across active Dolt product version, adapter
167+
implementation and version, and specification commit before mutation.
168+
- Explicit catalog initialization/migration through `initialize_catalog` and
169+
`openstatspec init`; data operations never auto-create catalog relations.
170+
- Read-only Dolt working-set evidence through `dolt_state_snapshot` and
171+
`openstatspec dolt-state`; core performs no Dolt version-control mutations.
126172
- Preflight checks for target profile limits, atomic imports, validation, a
127173
command-line interface, and machine-readable capability and loss reports.
128174

@@ -135,3 +181,11 @@ SPSS profile.
135181
- Multiple-response sets, variable alignment, variable sets, and custom
136182
attributes have explicit capability diagnostics; see the SAV profile.
137183
- Encrypted SPSS files are not supported.
184+
- Dolt adapter-envelope values are not claims about Dolt server limits. Failure
185+
recovery is compensating and reports deterministic residual inventory; a
186+
verified catalog receives a best-effort failed-operation audit when cleanup
187+
itself fails.
188+
- Export publication uses a durable, audit-recorded prior-file backup through
189+
SQL finalization. Post-success backup retention is reported separately and
190+
does not rewrite a successful export as failed; pre-success failures restore
191+
the prior destination and close the running operation as failed.

‎README.md‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,19 @@ implemented capability boundary.
6767

6868
## SPSS-like transformation frontend
6969

70-
The SPSS-like frontend lowers supported `RECODE`, `VARIABLE LABELS`, and
71-
`VALUE LABELS` syntax into a language-neutral canonical plan. The in-place
72-
path applies it to the same logical dataset, physical wide table, and metadata
73-
catalog without a derived dataset, copied table, snapshot, or separate
74-
rollback/history layer. Dolt remains the sole versioning layer for Dolt-backed
75-
edits, and the transformer never calls `DOLT_COMMIT`.
70+
The bounded SPSS-like frontend lowers `RECODE`, sequential `COMPUTE` and `IF`,
71+
`VARIABLE LABELS`, `VALUE LABELS`, numeric `FORMATS`, `VARIABLE LEVEL`, and
72+
`EXECUTE` into a language-neutral typed canonical plan. Conditions support
73+
parentheses, variable and numeric literal operands, comparisons, `AND`, and
74+
`OR`. String comparison and v0.2 string assignment currently fail closed.
75+
The in-place path applies the plan to the same logical dataset, physical wide
76+
table, and normative/compatibility metadata without a derived dataset, copied
77+
table, snapshot, or hidden history layer. Numeric targets may be created
78+
atomically on SQLite and PostgreSQL. MySQL, MariaDB, and Dolt fail closed on
79+
`target_mode=create` and require a separately provisioned physical and catalog
80+
target before assignment. Dolt requires the caller's exact clean branch/HEAD,
81+
leaves success as an inspectable working-set diff, and never calls
82+
`DOLT_COMMIT`.
7683

7784
See the [dataset transformations manual](docs/transformations.md) for schema
7885
installation, Python and CLI surfaces, database invariants, audit provenance,

‎docs/release-readiness.md‎

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 0.4.0 release readiness
1+
# 0.5.0 release readiness
22

33
This page records the expected release contract, not a publication event.
44
Creating a version tag remains a separate maintainer action.
@@ -67,6 +67,17 @@ The gate must prove that:
6767

6868
- a TransformationPlan object and its strict JSON mapping produce the same
6969
plan hash and in-place result;
70+
- the exact bounded `COMPUTE`/`IF` program compiles to all seven ordered
71+
operations without dropping `FORMATS`, `VARIABLE LEVEL`, or `EXECUTE`;
72+
- boolean data results match the equivalent expression and the target's label,
73+
0/1 value labels, `F1.0` print/write format, and nominal level exist in the
74+
normative catalog;
75+
- injected schema, data, catalog, and audit failures leave no partial apply;
76+
- compensation tracks only newly created targets and never drops or rewrites a
77+
pre-existing target;
78+
- MySQL, MariaDB, and Dolt reject create-target plans before mutation; their
79+
service evidence covers assignment to a separately provisioned physical and
80+
catalog target without schema DDL;
7081
- top-level SPSS compiler imports and legacy openstatspec.transform re-exports
7182
still load from an installed wheel;
7283
- install-in-place-schema, apply-plan, and apply-spss execute their documented
@@ -78,8 +89,11 @@ The gate must prove that:
7889
source/plan hashes and frontend contract, and contain no copied data;
7990
- no OpenStatSpec rollback, snapshot, staging, copy, derived-dataset, or
8091
parallel history artifacts are created; and
81-
- Dolt checks expected branch, HEAD, and a clean working set without committing
82-
or changing HEAD; other supported SQL connections remain allowed.
92+
- Dolt checks expected branch, HEAD, and a clean working set; success changes
93+
neither HEAD nor branch and never commits or resets; state is rechecked after
94+
the dataset lock and success must leave an inspectable working-set diff;
95+
other supported SQL connections remain allowed; and
96+
- string comparisons and v0.2 string assignments fail closed until exact
8397

8498
The built wheel must contain the generic openstatspec.transform modules and the
8599
implemented openstatspec.frontends.spss package. Stata and SAS remain empty
@@ -98,9 +112,12 @@ capability claim, or implied support.
98112
4. Build with `python -m build` and install the generated wheel in a clean
99113
environment.
100114
5. Confirm `openstatspec capabilities` reflects the intended support boundary.
101-
6. Confirm the release tag matches the package version and that CI, release
102-
fixtures, and capabilities use OpenStatSpec specification release `v0.2.0`
103-
at exact commit `79339ec3d8f8aa81789b7e85f6b8afa6f1374e50`.
115+
6. Confirm CI, release fixtures, and capabilities use the untagged OpenStatSpec
116+
specification release candidate at exact commit
117+
`f2fdf687d8cb32b944ca55a3e9e7215ffc603019`, publish
118+
`specification_status=release_candidate`, and leave
119+
`specification_release` null. If that exact commit receives a stable tag
120+
before this package is tagged, update the identity and re-run every gate.
104121
7. Review this document, the README, and CHANGELOG for accurate scope.
105122

106123
The tag-triggered release workflow repeats the non-service test suite, builds

‎docs/transformations.md‎

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@ OpenStatSpec separates transformation syntax, canonical meaning, and database
44
mutation. This lets multiple language frontends produce the same plan without
55
coupling the executor to any one language.
66

7-
The implemented frontend accepts a small SPSS-like subset: `RECODE`,
8-
`VARIABLE LABELS`, and `VALUE LABELS`. Stata and SAS are not implemented.
7+
The implemented bounded SPSS-like frontend accepts `RECODE`, sequential
8+
`COMPUTE` and `IF`, `VARIABLE LABELS`, `VALUE LABELS`, numeric `FORMATS`,
9+
`VARIABLE LEVEL`, and `EXECUTE`. Predicates support typed variable/literal
10+
operands, parentheses, numeric comparisons, `AND`, and `OR`. String comparison
11+
and v0.2 string assignment fail closed until exact profile-independent
12+
collation and explicit-width semantics are available; arbitrary SPSS, Python,
13+
and SQL expressions are rejected. Stata and SAS are not implemented.
914

1015
## Architecture
1116

@@ -106,9 +111,13 @@ result = openstatspec.apply_spss_in_place(
106111
dataset_id="responses",
107112
actor="agent@example.org",
108113
source_text="""
109-
RECODE age (18 THRU 34 = 1) (35 THRU 64 = 2).
110-
VARIABLE LABELS age 'Age group'.
111-
VALUE LABELS age 1 '18-34' 2 '35-64'.
114+
COMPUTE target = 0.
115+
IF (source_a = 1 AND source_b = 1) target = 1.
116+
VARIABLE LABELS target 'Example label'.
117+
VALUE LABELS target 0 'No' 1 'Yes'.
118+
FORMATS target (F1.0).
119+
VARIABLE LEVEL target (NOMINAL).
120+
EXECUTE.
112121
""",
113122
)
114123
```
@@ -130,20 +139,25 @@ On Dolt, also pass `--expected-branch` and `--expected-head`.
130139
Every successful apply preserves the logical `dataset_id` and physical
131140
schema/table identity. It creates no derived dataset, output table, full-table
132141
copy, staging table, snapshot, rollback artifact, or recovery/history layer.
133-
Existing-target recodes use direct `UPDATE`; label operations mutate existing
134-
catalog rows.
135-
136-
SQLite and PostgreSQL may add a numeric target where native transactions make
137-
the complete operation atomic. MySQL, MariaDB, and Dolt reject target-creating
138-
plans before the first mutation because implicit-commit DDL could leave a
139-
partial apply. Their target column and metadata must already exist.
140-
141-
Dolt is the sole history, diff, branch, and rollback layer for Dolt-backed
142-
datasets. Before mutation, the executor verifies the expected branch and
143-
`HEAD` and requires clean `dolt_status`. Success changes the same working set
144-
without changing `HEAD`. OpenStatSpec does not call `DOLT_COMMIT`, switch
145-
branches, merge, reset, tag, or create a hidden recovery commit. The caller
146-
reviews `dolt diff` and separately decides whether to commit or restore.
142+
Assignments and recodes use ordered `UPDATE` statements; later operations see
143+
earlier results. Label, value-label, format, and measurement-level operations
144+
update the normative catalog.
145+
146+
A numeric create target is supported atomically on SQLite and PostgreSQL.
147+
MySQL, MariaDB, and Dolt reject `target_mode=create` before mutation. On those
148+
profiles a separate versioned stage must first provision the nullable numeric
149+
physical column and normative variable row; the transformation executor then
150+
sees a pre-existing target and performs no schema DDL.
151+
The public operation reports success only after physical data, normative
152+
metadata, and the compact audit row are mutually complete.
153+
154+
Before Dolt mutation, the executor verifies the expected branch and `HEAD` and
155+
requires clean `dolt_status`. Success changes the same working set without
156+
changing `HEAD`; OpenStatSpec does not call `DOLT_COMMIT`, `DOLT_RESET`, switch
157+
branches, merge, tag, or create a hidden recovery commit. It rechecks branch,
158+
HEAD, and a clean working set after locking the dataset and immediately before
159+
mutation. The caller reviews a successful `dolt diff` and
160+
separately decides whether to commit or restore.
147161

148162
## Audit and provenance
149163

‎pyproject.toml‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "openstatspec"
7-
version = "0.4.0"
7+
version = "0.5.0"
88
description = "Reference adapter for the OpenStatSpec relational contract"
99
readme = "README.md"
1010
requires-python = ">=3.11"
@@ -22,7 +22,10 @@ openstatspec = "openstatspec.cli:main"
2222
[tool.pytest.ini_options]
2323
testpaths = ["tests"]
2424
addopts = "-q"
25-
markers = ["services: requires a configured PostgreSQL, MySQL, MariaDB, or Dolt service"]
25+
markers = [
26+
"services: requires a configured PostgreSQL, MySQL, MariaDB, or Dolt service",
27+
"candidate_evidence: non-claiming manual probe excluded from release gates",
28+
]
2629

2730
[tool.hatch.build.targets.wheel]
2831
packages = ["src/openstatspec"]

‎src/openstatspec/__init__.py‎

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,44 @@
22

33
from .api import (
44
apply_spss_in_place, apply_transformation_plan_in_place,
5-
capabilities, capability_matrix, derive_sql_dataset,
6-
execute_sql_transformation,
7-
export_sav, get_dataset, import_sav, inspect,
8-
install_in_place_transformation_schema, list_datasets,
5+
capabilities, capability_matrix, derive_sql_dataset, dolt_state_snapshot, execute_sql_transformation,
6+
export_sav, get_dataset, import_sav, initialize_catalog, inspect, list_datasets,
7+
install_in_place_transformation_schema,
98
register_sql_transformation, reconcile_derived_removals,
109
reconcile_sql_transformation_runs,
1110
remove_derived_physical_relation, retire_derived, validate, validate_derived,
1211
)
1312
from .core import CapabilityDeclaration, LossReport, UnsupportedOperationError
14-
from .sql.workflow import TransformationError
1513
from .frontends.spss import SpssFrontendCompilation, compile_spss_syntax
14+
from .sql import DoltConformanceSource
15+
from .sql.workflow import TransformationError
1616
from .transform import (
17+
AssignOperation, BooleanExpression, ComparisonExpression,
18+
ConditionalAssignOperation, ExecuteOperation, Operand, PredicateExpression,
1719
RecodeMatch, RecodeOperation, RecodeResult, RecodeRule,
18-
ReplaceValueLabelsOperation, SetVariableLabelOperation,
20+
ReplaceValueLabelsOperation, SetFormatOperation,
21+
SetMeasurementLevelOperation, SetVariableLabelOperation,
1922
TransformationFrontendError, TransformationPlan, TypedValue, ValueLabel,
2023
VariableDefinition, VariableSchema, transformation_plan_from_dict,
2124
)
2225

2326
__all__ = [
24-
"CapabilityDeclaration", "LossReport", "SpssFrontendCompilation",
25-
"TransformationError", "TransformationFrontendError",
27+
"AssignOperation", "BooleanExpression", "ComparisonExpression",
28+
"ConditionalAssignOperation", "ExecuteOperation", "Operand",
29+
"PredicateExpression",
30+
"CapabilityDeclaration", "DoltConformanceSource", "LossReport",
31+
"SpssFrontendCompilation", "TransformationError",
32+
"TransformationFrontendError",
2633
"RecodeMatch", "RecodeOperation", "RecodeResult", "RecodeRule",
27-
"ReplaceValueLabelsOperation", "SetVariableLabelOperation",
34+
"ReplaceValueLabelsOperation", "SetFormatOperation",
35+
"SetMeasurementLevelOperation", "SetVariableLabelOperation",
2836
"TransformationPlan", "TypedValue", "ValueLabel",
2937
"VariableDefinition", "VariableSchema", "transformation_plan_from_dict",
30-
"UnsupportedOperationError", "capabilities", "capability_matrix",
3138
"apply_spss_in_place", "apply_transformation_plan_in_place",
32-
"compile_spss_syntax", "derive_sql_dataset",
33-
"execute_sql_transformation", "export_sav",
34-
"get_dataset", "import_sav", "inspect",
35-
"install_in_place_transformation_schema", "list_datasets",
39+
"compile_spss_syntax", "install_in_place_transformation_schema",
40+
"UnsupportedOperationError", "capabilities", "capability_matrix",
41+
"derive_sql_dataset", "dolt_state_snapshot", "execute_sql_transformation", "export_sav", "get_dataset",
42+
"import_sav", "initialize_catalog", "inspect", "list_datasets",
3643
"register_sql_transformation", "reconcile_derived_removals",
3744
"reconcile_sql_transformation_runs",
3845
"remove_derived_physical_relation", "retire_derived", "validate",

0 commit comments

Comments
 (0)