Date: 2026-07-24
Branch: codex/data-rule-execution-m3a-m5
Task 7 establishes a fail-closed path from an AI-authored natural-language candidate to an immutable published RuleVersion. Neither caller-supplied compile evidence nor caller-supplied test evidence can advance lifecycle state.
The delivered chain is:
/interpret resolves distinct server-owned input/output schema snapshots,
a required input sample, and an optional golden output before invoking the
authoring model.draft RuleVersion plus an immutable validation profile.ArtifactStore and the bounded worker.draft -> validated and logical
plan compiled -> tested.published.compiled -> tested -> published. They must reference a published
RuleVersion and its successful logical test evidence.The public catalog returns published rules only. DataFlow release continues to consume published plans only, so Task 7 does not weaken the Task 4–6 execution boundary.
GenerationReceiptSigner signs a compact, closed JSON claim set containing:
Receipts use the dedicated required RULE_GENERATION_RECEIPT_SECRET and never
fall back to the Flask session secret. Issued-at, expiry, maximum lifetime, and
clock skew are checked cryptographically and against current database time
during consumption.
Draft creation performs SELECT ... FOR UPDATE on the generation record and a
compare-and-set consumption update. A new consumption is rejected if it is expired,
tampered, belongs to another actor, has a different source/RuleSpec/candidate
or validation context, has already been consumed, or is linked to another
version.
An exact same-actor receipt replay after an unknown commit outcome returns the
already-linked canonical draft even after the short-lived receipt expires.
This replay path still requires the same signed receipt hash and every canonical
claim to match; expiry is bypassed only after the consumed row is proven exact.
New consumption uses PostgreSQL clock_timestamp() rather than the
transaction-start timestamp, so a long-running transaction cannot extend a
receipt's usable lifetime.
Publication locks the rule/plan/profile/schema or plan/component/rule/binding/schema rows in one transaction and re-reads the current compiler, RuleSpec, plan, schema, binding, data source, object, dialect, evidence, and audit chain. Same-actor retries after an unknown commit outcome return the canonical published result; a different actor cannot use that replay path. Real PostgreSQL concurrency tests use independent threads, connections, and sessions: same-actor contenders return one canonical result and one audit, while a different actor is rejected after the winning commit.
The authoring agent now has a deterministic maximum of two repair attempts. Each attempt persists:
The prompt hash is calculated from the actual full prompt sent for that attempt, including the output JSON schema and accumulated repair messages. The final generation receipt therefore binds the final repair prompt, not the initial prompt.
Only malformed JSON and closed candidate-contract errors are repairable. Ambiguity and low confidence return clarification requirements immediately. Permission failures, destructive scope, lifecycle failures, and execution failures are never sent into the automatic repair loop.
Logical compile evidence binds the RuleVersion, compiler version, exact plan hash, schema hashes, capabilities, and actor. Logical test evidence additionally binds the exact trusted run ID and server-generated attestation:
All four evidence writers use INSERT ... RETURNING. On a uniqueness conflict
they load the canonical database row, compare the complete security-relevant
content, and return the real existing evidence ID; divergent replay fails
closed.
The logical runner supports assert with both reject and quarantine.
Quarantined rows are removed from the primary output, counted separately as
rows_quarantined, and are not misreported as rejected. The compiler version
was advanced to dataops-polars-1.43.0 for this result-contract change.
Physical SQL preflight validates the exact SQLGlot-bound plan and executes only
EXPLAIN <compiled INSERT ... SELECT ...> through the governed data-source
manager. It never uses ANALYZE and never executes the DML. The resulting
evidence contains dialect, statement digest, and EXPLAIN digest. Real
PostgreSQL and MySQL tests assert that destination row counts remain zero.
Physical Polars preflight stages the latest trusted, ready, unexpired input artifact and executes the exact bound plan in the isolated worker. Both physical backends are rejected on current schema, binding, dialect, plan-hash, RuleVersion, or logical-evidence drift.
Logical dry-run supports a distinct output schema and optional golden Parquet
result. The server stages both artifacts and passes their local paths and
digests to one spawned worker governed by the plan's single RLIMIT/RSS/timeout
budget. That worker verifies the staged golden digest and compares exact
schema, row count, row order, null positions, and field values. The Flask
process does not materialize either Parquet result. Concurrent large-golden
tests fail closed inside the same resource boundary. The real
cross-schema acceptance reads name/mobile, derives name_copy, removes one
quarantined row, and compares the output to the pinned golden artifact.
Migration 20260723_200 is forward-only and adds:
The migration refuses to run when revision 190 contains any RuleVersion or
execution plan, including draft and validated rows, because no pre-Task-7 row
can possess the required generation, compile, test, and publication provenance.
Operators must export and remove all legacy rows, apply migration 200, and then
rebuild each rule through the Task 7 chain. Physical evidence carries an explicit
legacy_untrusted marker, and publication/Runner/DataFlow gates require it to
be false. Downgrade is deliberately rejected because deleting publication and
generation evidence would break audit and replay guarantees.
The following server-owned operations are available:
POST /api/rules/interpret;POST /api/rules/rule-versions;POST /api/rules/rule-versions/<id>/validate;POST /api/rules/rule-versions/<id>/test;POST /api/rules/rule-versions/<id>/publish;GET /api/rules/rule-versions/<id>/evidence;GET /api/rules/catalog;POST /api/rules/execution-plans/<id>/validate;POST /api/rules/execution-plans/<id>/test;POST /api/rules/execution-plans/<id>/publish.Request bodies and catalog query parameters use closed shapes. Compile/test endpoints do not accept caller evidence. Editor permissions cover validation/testing while publication keeps the existing administrative permission boundary. The documented canonical catalog path is implemented; the prior nested path remains a compatibility alias.
/interpret preflights the dedicated receipt signer before resolving context
or invoking the model, so missing configuration cannot incur model cost.
Local and production environment templates and deployment runbooks document
generation and coordinated rotation of RULE_GENERATION_RECEIPT_SECRET
without containing a real secret.
DataFlow release no longer trusts RuleVersion status alone. Its production asset loader requires the exact published logical plan, successful compile evidence, successful test evidence, matching plan/schema/capability hashes, and the matching publication audit. Revoked or drifted logical evidence makes the referenced rule unavailable and the release fails closed.
Fail-first evidence included:
Final verification:
39 passed;183 passed;126 passed;1 passed;PhysicalPlanPublicationService PostgreSQL/MySQL lifecycle, drift,
evidence replay, and PostgreSQL concurrency: 2 passed;632 passed, 29 skipped, 59 subtests passed;All checks passed!;git diff --check: passed;20260723_200 (head);3 passed.healthy, and
GET /api/system/health returned HTTP/application code 200 with database
and Neo4j healthy.Skipped tests are environment-gated integration suites. The required real PostgreSQL, MySQL, and MinIO acceptance tests above were run explicitly against the local Docker services and passed.
Task 7 records publication preflight run IDs and exact evidence in dedicated
logical/physical evidence tables. It does not fabricate production rule_runs
for logical validation because those rows require a real deployment and
component binding. Production execution evidence remains owned by the Task 6
Runner path after a physical plan is published.
Task 7 now supports separate input/output snapshots, cross-schema transformations, deployable secret templates, and a documented rotation procedure. The remaining scope is production operational rollout: multi-replica load and failure testing, monitoring/alert thresholds, and production canary/rollback evidence. Those operational gates are outside this local Docker acceptance and must be closed before a production environment is declared ready.