Date: 2026-07-23
Branch: codex/data-rule-execution-m3a-m5
Implementation commits before the third remediation: 69d83ed, f4d798b,
8bffdb6, c152e74. The third-remediation commit is the commit containing
this report revision.
8bffdb6 is the final hash of the second remediation commit. It replaces the
intermediate a17bcd0 after restoring the historical M3A acceptance document
byte-for-byte; no production or test behavior changed in that amend.
Lifecycle boundary: physical Polars plans are created as compiled; Task 5
does not add a production promotion API. The real integration inserts a
published fixture only to exercise the existing Runner publication gate.
Task 5 now provides a fail-closed PostgreSQL/MySQL → Parquet/MinIO → isolated Polars → cataloged Parquet path through the production repository, resolver, executor, HTTP token, and durable task-ledger combination.
The final implementation includes:
date(), timestamp(), timestamp cast, and
timestamptz cast;RLIMIT_AS, bounded execution time, and
fail-closed worker errors;pending → object upload → ready catalog handoff keyed by
(correlation_id, binding_id, artifact_kind), with the exact attested
binding_hash;The bound plan contains canonical RuleVersion, SchemaSnapshot, and
DatasetBinding hashes plus dataops-polars-1.42.1 provenance. It contains no
Python source, pickle, callable, module, caller-supplied path, arbitrary URL,
secret, or serialized Polars internal plan.
The second review was implemented as fail-first slices. Representative RED evidence captured during the work:
schema_fields.app/runner/polars_worker.py existed;
the first adapter reconstruction run then exposed one schema-order failure
(1 failed) because canonical fields are name-sorted while Parquet retains
physical column order. Validation was corrected to compare the closed field
set and exact per-field types.test_polars_cast_operations_carry_complete_typed_targets failed
(1 failed) with missing target_field.test_polars_expression_date_and_timestamp_use_plan_timezone failed
(1 failed) because timestamp() produced timezone-aware output for a
timezone-free timestamp contract.1 failed, 553 passed) after in-memory BytesIO staging was removed; the
regression check was changed to assert the stage implementation contains no
BytesIO.io, nested
with); both were fixed before final verification.Second-remediation GREEN:
57 passed in 4.17s.30 passed in 3.92s.1 passed in 2.03s.554 passed, 26 skipped, 59 subtests passed in 8.39s.All checks passed!.git diff --check: passed.docker compose -f deploy/docker/docker-compose.yml config -q: passed.The third remediation supersedes the second-remediation migration and handoff implementation described below wherever they conflict.
The third review was also implemented in fail-first slices:
f4d798b and the state change moved to new migration
20260723_150;pending row before the first MinIO PUT;finally.Third-remediation GREEN:
49 passed in 5.62s;ready row and binding_hash,
same-digest retry, different-digest CAS conflict: passed;564 passed, 26 skipped, 59 subtests passed in 9.01s;git diff --check: passed;docker compose -f deploy/docker/docker-compose.yml config -q: passed;git diff f4d798b -- migrations/versions/20260723_140_rule_run_artifacts.py:
empty.Migration 20260723_140 is historical and unchanged. It retains its original
unique constraint:
UNIQUE (correlation_id, binding_id, artifact_digest)
New forward-only migration 20260723_150:
binding_hash, handoff_status, ready_at, failed_at,
failure_code, and updated_at;ready with the current canonical binding hash;(correlation_id, binding_id, artifact_kind) and a reconciliation index.PostgresArtifactResolver.publish_path implements the durable protocol:
FOR SHARE, attest its
hash/object/access contract, and insert the exact pending catalog row;ready.Resolution sees only ready rows whose persisted binding hash still equals the
canonical binding hash. Same-triple/same-digest retries reuse the ready object
without uploading; different digests fail before upload. Upload failure removes
the exact object and pending row. A lost finalize acknowledgement is rechecked:
confirmed ready is success; unconfirmed state returns
commit_outcome="unknown" and deliberately does not delete the possibly
cataloged object.
reconcile-rule-artifacts --limit --grace-seconds processes only aged,
bounded candidates. It deletes missing pending rows, finalizes valid pending
objects only while the binding still matches, marks ready rows failed when
their object is missing or invalid, deletes invalid pending objects safely, and
deletes only old safe-key orphans after the grace period. Fresh objects,
referenced objects, and malformed/unowned keys are retained.
cleanup-rule-artifacts --limit uses a bounded
FOR UPDATE SKIP LOCKED selection, removes each expired store object, and
deletes its exact catalog row in the same database transaction scope. Unit
tests cover the CLI and object+row cleanup behavior.
The local database had an earlier applied empty copy of migration 140 with a manually altered constraint. It was explicitly not accepted as migration evidence. After verifying zero rows, only that empty table was rebuilt by rewinding its Alembic marker to 130 and running the formal 130 → original-140 → 150 chain. Independently, the automated acceptance test creates and drops a fresh temporary database and proves old140 → head without manual ALTER.
ArtifactStore.stage:
NamedTemporaryFile while hashing;pyarrow.parquet.ParquetFile;Full schema contracts are no longer base64-encoded into MinIO metadata. MinIO metadata contains only bounded digest/count/expiry/size fields, with a 2,048-character aggregate ceiling. PostgreSQL remains the canonical store for the complete schema contract.
The adapter stages paths but does not collect input frames. A spawn child:
RLIMIT_AS relative to baseline virtual memory;The parent independently watches incremental RSS with psutil, kills the child on limit breach, enforces a hard timeout, and returns only bounded metrics. Worker PID is deliberately not exposed in the public result.
Pipe creation, process construction, process.start(), message handling, and
resource enforcement now share one guarded lifecycle. Startup errors are
sanitized, both pipe endpoints are closed when created, and a started process
is killed/joined/closed from the unified finally.
Tests prove:
Portability boundary: spawn and psutil are portable, but the OS hard limit
requires Unix resource.RLIMIT_AS. Linux/macOS are supported targets. On a
platform without RLIMIT_AS, the worker fails closed instead of silently
running without an OS memory boundary.
Each compiled cast operation now contains an exact target_field. The shared
validator rejects a missing field, an unknown/extra shape, a target name/type
mismatch, incomplete Decimal precision/scale, or missing timestamptz timezone.
The worker no longer falls back to inferring cast details from output fields.
Executable tests verify:
Decimal(12, 2);Datetime(time_zone="Asia/Shanghai");timestamp;date(offset_value) converts to the rule timezone before selecting the
calendar date;timestamp(offset_value) converts to the rule timezone and removes timezone
metadata to match the platform's timezone-free timestamp type.The integration discovers runtime values from
deploy/docker/docker-compose.yml; credentials are not copied into source.
/v1/tasks/execute: signed short-lived task token;PostgresTaskLedger: single-use claim and committed success record.Evidence:
success / committed;schema_fields;The test finally removes only its exact correlation prefix, source tables, ledger record, catalog entries, plan/binding/deployment/rule rows, and schema snapshots, and asserts the MinIO prefix is empty.
docs/security/data-rule-runtime-sbom.json now uses standard CycloneDX fields:
licenses;externalReferences;properties;bom-ref and dependency relationships.Pinned runtime components:
polars==1.42.1 — MIT;polars-runtime-32==1.42.1 — MIT;pyarrow==21.0.0 — Apache-2.0;psutil==5.9.8 — BSD-3-Clause;minio==7.2.10 — Apache-2.0.The test validates the document with jsonschema against vendored official
CycloneDX 1.5, SPDX, and JSF schemas. The official files' SHA-256 hashes are
asserted before validation, so the check is deterministic and offline.
Production:
app/core/data_rules/compilers/polars.pyapp/runner/artifacts.pyapp/runner/polars_worker.pyapp/runner/rule_polars.pyapp/runner/bootstrap.pymigrations/versions/20260723_140_rule_run_artifacts.pymigrations/versions/20260723_150_rule_artifact_handoff_state.pyrequirements.txtdocs/security/data-rule-runtime-sbom.jsondocs/security/cyclonedx-1.5-schema/Tests:
tests/core/data_rules/test_polars_compiler.pytests/runner/test_artifacts.pytests/runner/test_artifact_handoff.pytests/runner/test_polars_worker.pytests/runner/test_rule_polars.pytests/runner/test_bootstrap.pytests/integration/test_data_rule_polars_execution.pytests/integration/test_rule_artifact_migration_upgrade.pytests/test_data_rule_runtime_sbom.pytests/test_data_rule_schema.pyRulePlanExecutor continues to refuse compiled plans in the published
runtime path.quality_check remains fail-closed through the existing adapter; Task 5 does
not claim an artifact-backed quality-check implementation.ArtifactStore.read remains a compatibility/helper API that can return a
bounded in-process frame. The production Polars adapter does not call it;
it uses staged files plus the isolated worker.RLIMIT_AS is unavailable.