# 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 ` 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//validate`; - `POST /api/rules/rule-versions//test`; - `POST /api/rules/rule-versions//publish`; - `GET /api/rules/rule-versions//evidence`; - `GET /api/rules/catalog`; - `POST /api/rules/execution-plans//validate`; - `POST /api/rules/execution-plans//test`; - `POST /api/rules/execution-plans//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.