task-7-report.md 7.9 KB

Task 7 Report — Trusted rule generation and publication gates

Date: 2026-07-24 Branch: codex/data-rule-execution-m3a-m5

Outcome

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:

  1. /interpret resolves a server-owned schema snapshot and optional server-owned sample artifact before invoking the authoring model.
  2. A ready rule candidate is persisted with its actor, source hash, candidate hash, RuleSpec hash, model hash, prompt hash, context hash, and all bounded repair attempts.
  3. The server issues a short-lived HMAC-SHA256 generation receipt over that closed claim set.
  4. RuleVersion creation locks the generation row, verifies every claim against canonical database state, consumes the receipt exactly once, and creates a draft RuleVersion plus an immutable validation profile.
  5. Logical validation compiles a closed, validation-only Polars plan against the pinned schema snapshot.
  6. Logical testing executes that exact plan in the isolated Polars worker against the pinned MinIO sample artifact. Digest, schema, resource, row, expiry, and ownership checks are enforced by ArtifactStore.
  7. Successful evidence advances RuleVersion draft -> validated and logical plan compiled -> tested.
  8. Publication requires exact successful compile and test evidence and advances both objects to published.
  9. Deployment-bound physical plans independently pass 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.

Receipt and concurrency boundary

GenerationReceiptSigner signs a compact, closed JSON claim set containing:

  • receipt version;
  • generation run ID;
  • actor UID;
  • source text hash;
  • candidate hash;
  • RuleSpec hash;
  • model hash;
  • prompt hash;
  • validation context hash;
  • issued-at and expiry timestamps.

Draft creation performs SELECT ... FOR UPDATE on the generation record and a compare-and-set consumption update. A receipt 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.

Publication uses row locks and state predicates. Same-actor retries after an unknown commit outcome return the canonical published result; a different actor cannot use that replay path.

AI repair boundary

The authoring agent now has a deterministic maximum of two repair attempts. Each attempt persists:

  • attempt number and outcome;
  • candidate hash;
  • deterministic error code;
  • model, prompt, and context hashes.

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 and physical evidence

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:

  • input artifact digest;
  • output artifact digest;
  • output schema hash;
  • violation digest;
  • bounded row and violation counts.

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.

Forward-only migration

Migration 20260723_200 is forward-only and adds:

  • corrected RuleVersion and execution-plan lifecycle constraints;
  • receipt hash, consumption timestamp, validation context, model hash, and prompt hash on generation runs;
  • one-time receipt/version uniqueness constraints;
  • persisted generation attempts;
  • immutable validation profiles;
  • logical plans and logical compile/test evidence;
  • physical compile/test evidence binding fields;
  • validation failures and publication audits;
  • the published-rule catalog index.

The migration preflights incompatible legacy lifecycle values before replacing constraints. Downgrade is deliberately rejected because deleting publication and generation evidence would break audit and replay guarantees.

API surface

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 use closed shapes. Compile/test endpoints do not accept caller evidence. Editor permissions cover validation/testing while publication keeps the existing administrative permission boundary.

Acceptance evidence

Fail-first evidence included:

  • publication rejected without exact compile and test evidence;
  • invalid, expired, replayed, actor-mismatched, source-mismatched, and RuleSpec-mismatched receipts;
  • concurrent receipt consumption;
  • caller-forged test evidence;
  • schema, binding, dialect, and plan drift;
  • bounded repair exhaustion and no-repair ambiguity;
  • initial quarantine compilation failure before support was added.

Final verification:

  • focused Task 7/compiler/API suite: 92 passed;
  • expanded data-rule and Runner regression: 284 passed;
  • real PostgreSQL + MinIO receipt/logical/physical-Polars lifecycle: 1 passed;
  • real PostgreSQL and MySQL SQL EXPLAIN preflight with zero destination writes: 2 passed;
  • combined real lifecycle and SQL integration files: 6 passed;
  • full repository suite: 616 passed, 29 skipped, 59 subtests passed;
  • changed-file Ruff: All checks passed!;
  • git diff --check: passed;
  • local PostgreSQL Alembic current/upgrade: 20260723_200 (head).

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.

Residual scope

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.

Logical validation currently pins one schema snapshot for both input and output. Rules that intentionally change their output schema require a future validation profile extension with distinct server-owned input and output snapshots. This does not affect same-schema cleaning rules, and the current implementation fails closed rather than guessing an output schema.