fix: unblock job1_prod (DQX quarantine schema) + rollback playbook and CI secret hardening - #52
Merged
Merged
Conversation
DQX 0.15.0 (#51) added rule_fingerprint, rule_set_fingerprint and skipped to the _errors/_warnings structs it appends to quarantined rows. The write to raw.order_quarantine used overwriteSchema=false, so the first prod run after the upgrade hard-failed with DELTA_METADATA_MISMATCH and skipped everything downstream. The shape of those structs is owned by the DQX library version, not by the source data contract, so pinning it turns every dependency bump into a prod outage — a false positive rather than the drift signal the guard exists to raise. Switch that one write to overwriteSchema=true, joining ops._health as the second intentional exception. The guard is not lost: the business columns reaching quarantine come from the same DataFrame written to raw.order a few lines later, which keeps overwriteSchema=false, so genuine source drift still hard-fails the same task. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merging to main deploys to prod with no approval gate, but nothing documented how to get back. Both the CI/CD article review and the official Databricks best-practices doc flag the missing rollback procedure as a real gap. The section is organised around the rule that matters: a redeploy reverts code, never data. Code rollback is a checkout of the last good sha plus make deploy env=prod, which works because nothing deployed is stored apart from the checkout — jobs.yml, the SDP pipeline and the dashboard are all regenerated, so git history is the artifact store. Data rollback is per-table Delta RESTORE, with the caveat that curated.order_enriched cannot be rebuilt by re-running: it freezes product_name at sale time, so re-deriving stamps today's names onto historical orders and is silently wrong. Schema rollback via make drop is last resort since it destroys history. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two mitigations for the long-lived DATABRICKS_CLIENT_SECRET, plus the context for why it is still there at all. The secret moves from the workflow-level env block, where it was visible to every step including dependency installation, to the three steps that actually talk to Databricks. Dependency install and unit tests are exactly where third-party code executes, and they never needed it. The host and client id stay at workflow level — they are identifiers, not credentials. Every third-party action is now pinned to a full commit SHA rather than a tag, since tags are mutable and whoever controls an action's repo can repoint one at code that then runs on the runner with that environment. databricks/setup-cli resolves the CLI version from a VERSION file at the pinned ref, so the SHA pin keeps the CLI on 1.9.0 exactly as the tag did — verified, not assumed. The architecture spec now records why OIDC / workload identity federation is the mechanism Databricks recommends instead, and why this repo cannot use it: the trust anchor is a service principal federation policy, an account-level API, and Free Edition has no account-level API access. The exact policy command and workflow delta are documented for anyone forking onto a paid account. Neither mitigation shortens the credential's lifetime; only OIDC does that. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
andre-salvati
added a commit
that referenced
this pull request
Jul 25, 2026
workflow.md said the schema-drift guard had a single exception (ops._health). There are two: raw.order_quarantine also writes with overwriteSchema=true, added in #52 and already documented in CLAUDE.md — the spec was not updated in the same commit, which is what its own "keep docs in sync" section exists to prevent. Also corrects the Co-Authored-By trailer, which named Opus 4.8. require-changelog-entry.sh printed guidance the spec had superseded: "at most 3 sentences" (dropped at #49 because it constrained nothing) and a header template built from the branch name. Both now match specs/workflow.md — a ~1000-character single paragraph and the PR-URL header — and the message links to the section so the two cannot drift silently again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> EOF --subject 'Commit the agent tooling: workflow hooks, three project skills, and a fully ignored reports/' --body-file /tmp/pr-body-c1_otrlp.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What?
Three related pieces of CI/CD and production hardening:
job1_prodhas failed since 2026-07-24 09:05 UTC.raw.order_quarantineis no longer schema-pinned.specs/workflow.md.Why?
The outage: DQX 0.15.0 (#51) added
rule_fingerprint,rule_set_fingerprintandskippedto the_errors/_warningsstructs it appends to quarantined rows. The write usedoverwriteSchema=false, so the first prod run after that merge hard-failed withDELTA_METADATA_MISMATCHand skipped every downstream task.The rollback gap: merging to
maindeploys to prod with no approval gate, and nothing documented how to get back. Flagged independently by a CI/CD article review and by the official Databricks best-practices doc.The secret:
DATABRICKS_CLIENT_SECRETsat in the workflow-levelenvblock, visible to every step — including dependency installation, which is exactly where third-party code runs.How?
The quarantine struct shape is owned by the DQX library version, not by the source data contract, so pinning it turns every dependency bump into a prod outage — a false positive, not the drift signal the guard exists to raise. The guard isn't lost: the business columns reaching quarantine come from the same DataFrame written to
raw.ordera few lines later, which keepsoverwriteSchema=false.The rollback section is organised around one rule — a redeploy reverts code, never data. Code rollback works because nothing deployed is stored apart from the checkout (
jobs.yml, the SDP pipeline and the dashboard are all regenerated), so git history is the artifact store.Actions are pinned by SHA because tags are mutable.
databricks/setup-cliresolves the CLI version from aVERSIONfile at the pinned ref — verified it still installs 1.9.0, not assumed.The architecture spec records why OIDC would be the real fix and why this repo can't use it: the trust anchor is an account-level API, and Free Edition has none. Neither mitigation shortens the credential's lifetime; only OIDC does.
Validation?
Unit tests pass (16); ruff lint + format clean. The staging integration test in this PR's CI exercises
extract_source2end-to-end, which is the actual regression path. Theonpush.ymlchanges are validated by this very run — an auth or SHA mistake shows up as a red CI here rather than onmain.Impact in prod
raw.order_quarantinegains three nested fields inside_errors/_warnings(DQX framework metadata; no business column changes).overwriteSchema=true, so it self-heals instead of failingNo consumer reads
raw.order_quarantine— not the dashboard, not the SDP pipeline. Business columns are untouched.Chosen strategy: leave as-is — the next
job1_prodrun rewrites the table schema and recovers on its own. This PR ends an outage rather than risking one.