Crosshire
← Handbook
RunRunbooks · incidents · game day

13Operations

This route decides who gets woken up, what they read once they are, and what they may do without asking anybody. The load-bearing artefact is not an incident process document — it is one line per pipeline saying whether a re-run is safe, written months before anyone needs it at 03:00.

Stand 29.07.2026

Runbooks

A runbook is not documentation. It is a script for a tired person with a phone, and it is only correct if someone who was actually woken up wrote it.

#One page, written by whoever gets woken

What
One runbook per alert, not per system. The alert names the runbook, the runbook names the first query, and the first query settles the only question the responder has: are the numbers wrong, or is this a late job that will be fine by breakfast?
text
rb_silver_sales_order_valid_amount.md — the whole page, one alert
RUNBOOK  silver_sales_order          owner: grp_dwh_engineers
alert    "expectation valid_amount: failed_records > 0"
page     yes, 24/7          (wrong numbers, not late numbers)

1  WHAT BROKE
   The Lakeflow pipeline dropped order lines violating business
   rule 5: amount_eur > 0 for non-cancelled lines. Usual cause:
   silver.fx_rate has no CHF row for the order date, so
   amount_eur comes out NULL.

2  FIRST QUERY  (SQL warehouse "ops", catalog dwh_prod)
   SELECT l.order_ts::DATE AS d, l.currency, COUNT(*) AS lines
   FROM   dwh_prod.silver.sales_order_line l
   LEFT   JOIN dwh_prod.silver.fx_rate r
          ON  r.currency  = l.currency
          AND r.rate_date = l.order_ts::DATE
   WHERE  r.currency IS NULL
     AND  l.order_ts >= current_date() - INTERVAL 3 DAYS
   GROUP  BY 1, 2 ORDER BY 1;

3  DECIDE
   rows returned -> missing FX. Load the rate, then re-run.
   zero rows     -> not FX. Escalate, do NOT re-run.

4  RE-RUN?   safe blind. Full refresh: NO — see re-run table.
5  ESCALATE  after 45 min to grp_dwh_engineers rota #2.
6  TELL      #dwh-status, before you start, not after you finish.
Six blocks, one screen, no prose. The architecture, the rationale and the history of the pipeline belong elsewhere — a page that has to be scrolled is a page that gets skimmed.
Why here
Runbooks written by the architect fail predictably: they omit the steps she performs without noticing. Which warehouse. Which catalog. Where the pipeline event log is, as opposed to the job run output. The responder stalls at step 2, and those twenty minutes are twenty more minutes of a wrong number being published.
Someone who was woken up writes a different page. They write the catalog name, because they typed it wrong once at 03:00. Being paged is the only reliable way to find out which knowledge was tacit.
Doing it
  • One runbook per alert. A single pipeline can fail three ways that need three different first queries.
  • Put the runbook link in the alert payload. A runbook found by searching a wiki is a runbook read after the incident.
  • Start with a copy-pasteable query returning rows or zero rows, and say what each outcome means.
  • Give the escalation a timer in minutes and a name. 'Escalate if needed' means nobody escalates.
Embedding it
  • Do not write the first runbook. Wait for the first real page, then have the person who took it write it while they are still annoyed.
  • Have someone who has never touched that pipeline follow the page literally, using no knowledge that is not written on it. Every place they stop is a missing line.
  • In review ask only 'could you run this at 03:00 with no context?' — checkable, unlike 'is it clear?'.
  • Rotate the rota before handover, so gaps are found by four people rather than by the author.
Defaults
  • One page, six blocks: what broke, first query, decide, re-run answer, escalate, tell.
  • Runbook link in the alert payload; runbook file in the repo beside the pipeline.
  • First step is always a query with a stated interpretation, never 'check the logs'.
  • Written by the responder, reviewed by the pipeline's author — that direction, not the reverse.
Gotchas
  • A step that says 'check the logs'. Three logs could be meant — pipeline event log, job run output, driver log — and at 03:00 the responder opens the wrong one first. Name the log and the filter.
  • Runbooks in a wiki space nobody has bookmarked. The symptom is an incident handled entirely from memory and Slack, followed by 'we have a runbook for that' the next morning.
  • One runbook per pipeline. It becomes a decision tree, the responder reads the wrong branch, and the first query answers a question they did not have.
  • A runbook never executed by anyone but its author. It reads complete and is missing a catalog name or a permission the rota does not hold — which surfaces as access-denied, mid-incident.

#The re-run question

What
Every scheduled job and pipeline carries one pre-agreed answer to a single question: is it safe to re-run this blind, or does it need intervention first? Three answers are allowed — yes, yes-if, no. It is decided at build time by whoever wrote the write logic, not at 03:00 by someone who did not.
The re-run table — one row per scheduled thing, kept in the repo
Job / pipelineWrite patternRe-run blind?If you do it anyway
bronze.shop_orderAutoloader append, checkpointedyesNothing — the checkpoint skips seen files. Full refresh is a separate question and the answer is no: it re-lists the Volume and duplicates every row on an append-only bronze.
bronze.sap_kna1daily full snapshot, append-onlyno — check firstA second batch lands for the same logical day. Snapshot comparison then diffs today against today, finds nothing, and writes no SCD2 version for customers that did change.
silver.customerSCD2 mergeyes, if as_of is a parameterWith current_timestamp() the re-run closes the interval it opened four minutes ago and opens another. Two versions, one business change, _tech_is_current still true on exactly one row.
silver.sales_order_lineSCD1 full overwriteyesNothing. Overwrite is idempotent by construction — the only row whose answer needs no caveat.
gold.fct_order_lineMERGE on order_line_skyes, if silver is completeAgainst a half-written silver it upserts a partial day, and gold disagrees with silver with no error anywhere. Duplicate order_line_sk aborts the MERGE on multiple source matches — the loud, good case.
gold.agg_revenue_monthINSERT INTO … REPLACE WHERE month = :monthyes, if REPLACE WHERE is on the statementWritten as a bare INSERT OVERWRITE it replaces the WHOLE table with the one month in the SELECT — every other month gone in a single commit, no error. The table is liquid-clustered and unpartitioned, so partitionOverwriteMode=dynamic does not scope it either; REPLACE WHERE is the only mechanism that does. Scoped too widely, it re-states closed months from whatever gold.fct_order_line holds now, moving last quarter's published revenue after the quarter closed.
Three of those are 'yes' only because of a decision in the code. The SCD2 row is the sharpest: the clock arrives as a job parameter, or the answer flips to no.
python
src/dwh/jobs/silver_customer.py — the injected clock is what makes the answer 'yes'
def run(spark, catalog: str, as_of: str) -> int:
    """Rebuild silver.customer as SCD2. Returns rows in the table after.

    as_of is a JOB PARAMETER, never current_timestamp(). Re-running the
    same logical day must produce identical intervals; a wall clock read
    inside the job turns every re-run into a new version.
    """
    target = f"{catalog}.silver.customer"
    incoming = clean_customer(spark.read.table(f"{catalog}.bronze.sap_kna1"))

    new_state = apply_scd2(                       # -> DataFrame, full new state
        current=spark.read.table(target),
        incoming=incoming,
        keys=["customer_id"],
        as_of=as_of,
    )

    # Delta refuses to overwrite a table that is also being read from, and
    # new_state reads target. Stage, then swap — INSERT OVERWRITE keeps the
    # table's comment, clustering and grants; CREATE OR REPLACE loses them.
    stage = f"{catalog}.silver.stg_customer_scd2"
    new_state.write.mode("overwrite").saveAsTable(stage)
    spark.sql(f"INSERT OVERWRITE TABLE {target} SELECT * FROM {stage}")
    spark.sql(f"DROP TABLE {stage}")
    return spark.table(target).count()
sql
The 'check first' query for bronze.sap_kna1 — step 2 of its runbook
SELECT _tech_batch_id,
       COUNT(*)             AS rows_landed,
       MAX(_tech_loaded_at) AS loaded_at
FROM   dwh_prod.bronze.sap_kna1
WHERE  _tech_loaded_at >= current_date()
GROUP  BY _tech_batch_id
ORDER  BY loaded_at DESC;

-- one batch id  -> extract landed once, re-run is safe
-- two batch ids -> already re-run. Do NOT repeat; resume at silver.
Why here
At 03:00 the responder will re-run something. The only question is whether they know the consequence or are hoping there is not one — and hope is the default when nobody wrote the answer down, because a re-run is the one action available to someone with no context, and it usually works.
'Usually' is the problem. Every failure in the table above is silent: a duplicate batch, a missing SCD2 version, a re-stated month. Nobody finds it that night — they find it in a quarterly comparison eleven weeks later, when the cause is no longer recoverable from memory.
Doing it
  • Add the re-run row in the same pull request as the job. Retro-fitting it means re-deriving intent from someone else's code.
  • Inject every clock — as_of, run_date, batch_id are parameters, never read from the wall clock inside a transform.
  • Give each yes-if row its check query verbatim in the runbook. A condition that takes thinking to verify gets skipped.
  • Prove the 'yes' rows in CI: run twice, assert identical — see the warehouse-invariants route.
Embedding it
  • Do not fill the table in for them. Ask 'what happens if this runs twice?' in each job's review; the rows nobody can answer are the ones that will hurt.
  • Have them break one in dwh_test: load the snapshot twice, then find the missing SCD2 version. They will remember the shape of a silent failure.
  • Make the re-run row a definition-of-done item, so it is refused at review rather than requested later.
  • When someone marks a job 'no', make them write the intervention. 'No' alone just moves the 03:00 decision to a person with less information.
Defaults
  • Three answers only: yes, yes-if-, no-with-procedure. No prose.
  • The table lives in the repo and is reviewed with the code, not in an operations wiki.
  • Re-run and full refresh answered separately for every pipeline.
  • Idempotency proven by a test, never asserted in a table.
Gotchas
  • current_timestamp() inside an SCD2 merge. Every re-run opens a version for rows that did not change, _tech_is_current stays correct, nothing asserts. You find it when someone asks how often a customer changed segment and the answer is 'nine times, all on 14.03.2026'.
  • Treating full refresh as a stronger re-run. On a @dp.table streaming table it clears checkpoint state and reprocesses from source — duplicating append-only bronze, and silently losing rows where source retention is shorter than the window.
  • Marking a job safe because the last three re-runs were fine. Safety is a property of the write pattern, not of the sample: an overwrite is safe, an append is not, however often it has been repeated without incident.
  • Re-running a downstream job while the upstream is half-written. gold.fct_order_line merges a partial silver day, nothing errors, and gold and silver now disagree behind a green run history.
  • Believing a WHERE clause scopes an INSERT OVERWRITE. It does not: the predicate filters the SELECT, the overwrite still replaces the whole table, so one re-run of gold.agg_revenue_month leaves exactly one month in it. On a liquid-clustered table there are no partitions for partitionOverwriteMode=dynamic to spare either. You notice when a year-on-year chart renders a single bar, and the fix is a backfill, not a rollback, if VACUUM already ran.

Incidents

The warehouse-specific part of incident response is the severity ladder: wrong numbers outrank late numbers, and most teams have it backwards because only the late ones page.

#Severity, blast radius, and the first ten minutes

What
Three severities, defined against this warehouse rather than a generic P1/P2/P3 ladder:
  • S1 — published numbers are wrong. gold.fct_order_line or gold.mv_sales is serving revenue that violates a business rule, and a dashboard has already shown it. Page at any hour.
  • S2 — data is stale past its SLA. gold.fct_order_line has a 26-hour freshness SLA and has breached it. Page in business hours: the numbers are right, just old, and the timestamp is visible.
  • S3 — a job failed, the data is correct and inside SLA. A retry handled it, or the next run will. Ticket, no page.
The ordering matters because the alerting is naturally the wrong way round: a failed job pages loudly, a wrong number pages not at all unless someone built the check. A team that alerts only on job failure has automated S2 and S3 and left S1 to whoever notices.
The first ten minutes of an S1 are the same three moves every time — stop publishing, measure, then diagnose:
sql
Stop the bleeding, then find out how far it reached
-- 1. pause the downstream refresh FIRST. Diagnosing while a scheduled
--    BI refresh republishes the wrong number turns a one-hour incident
--    into a one-day one.

-- 2. how bad, and since when
DESCRIBE HISTORY dwh_prod.gold.fct_order_line LIMIT 10;

SELECT COUNT(*)      AS bad_lines,
       MIN(order_ts) AS first_bad,
       MAX(order_ts) AS last_bad
FROM   dwh_prod.gold.fct_order_line
WHERE  status <> 'CANCELLED'
  AND (amount_eur IS NULL OR amount_eur <= 0);   -- business rule 5

-- 3. blast radius: who read it while it was wrong. Filter on event_date,
--    not event_time: the table is laid out by date, and event_time scans
--    the whole of it — a full scan you are paying for mid-incident.
SELECT DISTINCT entity_type, entity_id, target_table_full_name
FROM   system.access.table_lineage
WHERE  source_table_full_name = 'dwh_prod.gold.fct_order_line'
  AND  event_date >= current_date() - INTERVAL 7 DAYS;
--    a NULL target is a notebook, dashboard or ad-hoc read. Still a consumer.

-- 4. if a bad version was published, roll back — do not patch
RESTORE TABLE dwh_prod.gold.fct_order_line TO VERSION AS OF 412;
Lineage answers "who read it", not "who believed it", and it emits records only where lineage can be inferred — an empty result is not proof that nothing read the table. The limits, including the rolling one-year window on the system tables, are in Audit & lineage. Treat the result as a floor and notify wider.
Why here
Wrong numbers cost trust, and trust does not restore with the table. A revenue figure that was wrong for six hours and got quoted in a meeting is fixed by telling the people who quoted it, not by correcting the table. That is why blast radius is a step and not an afterthought.
Pausing before diagnosing feels backwards and is the highest-value ten seconds available. Every minute of diagnosis with the refresh still scheduled adds another cohort holding a wrong number and no reason to doubt it.
Doing it
  • Define the three severities against real tables and real SLAs, and write down which one pages at 03:00.
  • Split the roles: one on the keyboard, one running the incident, one on comms. Below three people the lead is still a role — held by whoever is not typing.
  • Pause downstream refresh as move one, and record the pause so it gets undone.
  • Prefer RESTORE to a corrective UPDATE. A rollback is one auditable operation; a patch is a new transformation written under time pressure with no test.
Embedding it
  • Give the incident-lead role to a team member on the first real S2, with you in the room and silent. Taking the keyboard once teaches them you are the escape hatch.
  • Have the team classify the last ten failures into S1/S2/S3 from run history. The argument about which are S1 is the training.
  • Ask 'who is holding a wrong number right now?' in every incident until someone else asks it first.
  • Practise the rollback in dwh_test until RESTORE is boring. Nobody runs an unfamiliar command on production mid-incident.
Defaults
  • Severity defined by consequence to the data, not by which component failed.
  • Pause, measure, diagnose — in that order, every time.
  • Rollback over patch; a corrective UPDATE gets the same review as any other transformation.
  • Blast radius from lineage treated as a lower bound, never as a complete list.
Gotchas
  • Diagnosing before pausing the refresh. The dashboard republishes the wrong number every fifteen minutes while you work, so the population who need telling afterwards grows for the whole duration of the incident.
  • Debugging in the incident channel. Stack traces and the narrative compete for one scroll, the business reader stops following, and starts asking the incident lead directly — which removes the incident lead.
  • Nobody declares the incident over. The paused refresh stays paused, the dashboard is stale for a week, and the next complaint is about the fix rather than the fault.
  • Assuming an empty lineage result means nothing consumed the table. Lineage is emitted only where it can be inferred; a downstream extract nobody registered will not appear, and you will not notify its owner.

#Game day, week 13

What
Three hours in dwh_test, with real code and real alert routing. You break things on purpose and do not touch the keyboard. Week 13 because it is a month past the point where the team's contribution overtakes yours — late enough that they built this, early enough that you are still here to fix what the day exposes.
  1. The SAP extract never arrives — no file at 04:00 for bronze.sap_kna1. Does anything alert, or does the pipeline succeed on yesterday's data?
  2. The extract arrives twice: two batch ids for one logical day. Does anyone notice the missing SCD2 versions?
  3. silver.fx_rate has no CHF row for one day, so amount_eur goes NULL for Swiss lines and breaks business rule 5. Which fires — the expectation, the freshness check, or nobody?
  4. SAP adds a column to VBAP. Does the schema-drift check catch it, and does the pipeline keep running?
  5. The gold MERGE is killed mid-run. Is gold.fct_order_line consistent, and does the runbook's re-run answer hold?
  6. The pipeline's author is 'on holiday' and may not speak for three hours. This is the scenario that tests the runbooks.
Scenario six is the one people want to drop and the only one that measures anything. The rest test the alerting; the silent author tests whether the knowledge ever left their head.
Why here
Runbooks decay invisibly: the alert text drifts, a warehouse is renamed, a permission is tightened. None of that fails a test. It produces a page that reads perfectly and does not work, and you find the stale sentence only by following it under pressure.
The day also answers a question no status report answers honestly — whether the team can operate this without you. Three hours watching them fail to find the event log is worth six weeks of nodding in a workshop.
Doing it
  • Three hours, dwh_test, alerts routed to the real rota. A muted workspace teaches nothing about routing.
  • Seed the failures from a fixtures script so the day is reproducible and can be repeated after handover.
  • Time each scenario: time to alert, time to correct diagnosis, time to recovery. Those three numbers are the deliverable.
  • Every gap found becomes a pull request that week, with a named owner, before anyone goes home on Friday.
Embedding it
  • Sit on your hands. Rescuing at minute ten converts a diagnostic into a demonstration of your value, which is the opposite of the goal.
  • Let the team choose two of the six scenarios. What they pick shows where they feel exposed; what they avoid shows more.
  • Have the team run the debrief and write the actions. If you write the actions, they are your actions.
  • Ask which scenario they would not survive alone at 03:00. The honest answer is the plan for the next four weeks.
Defaults
  • Three hours, six scenarios, dwh_test, real alerts, consultant silent.
  • Measure time-to-alert, time-to-diagnosis, time-to-recovery per scenario.
  • Seeded from a script, so it repeats rather than being an event.
  • One scenario always removes the author of the thing being broken.
Gotchas
  • Running it where alerting is disabled 'to avoid noise'. You then measure diagnosis and learn nothing about routing — and routing is what is broken in most teams, because it was configured once and never received a real page.
  • Announcing the scenarios in advance. The team pre-reads the runbooks, every timer looks good, and the exercise certifies a state that will not exist at 03:00.
  • Choosing scenarios everyone already handles. A game day that finds nothing was designed to find nothing; a debrief with no actions means the scenarios were too kind.
  • Doing it in week 24. Whatever it exposes is now a handover risk instead of a coaching opportunity, with no time to fix it with the team rather than for them.

#The note lands in the runbook, or it never lands

What
The rule is mechanical: the fix and the runbook edit are the same pull request. Not a follow-up ticket, not an action item with a due date. As separate work it competes with feature work, and it loses every time.
text
What the incident of 03.07.2026 added to rb_silver_sales_order_valid_amount.md
  2  FIRST QUERY  (SQL warehouse "ops", catalog dwh_prod)
+       -- the rota needs SELECT on silver.fx_rate; PERMISSION_DENIED
+       -- here means you are not in grp_dwh_engineers.
        SELECT l.order_ts::DATE AS d, l.currency, COUNT(*) AS lines
        ...

  3  DECIDE
        rows returned -> missing FX. Load the rate, then re-run.
        zero rows     -> not FX. Escalate, do NOT re-run.
+       ALL currencies missing for a date -> the ECB CSV was empty,
+       not late. Loading it again will not help. Escalate at once.

  seen: 03.07.2026, 04:12-05:35. Detected by the expectation, not by
  the freshness alert — freshness would have fired at 06:00.
Two added lines and a timestamp. No root-cause essay, no five whys, no separate document. What earns its place is what the last responder did not know and the next one will need: the missing permission, the uncovered branch, and how long it really took.
Why here
Post-mortems written a week later document a sequence that did not happen. People reconstruct a tidy order from memory, and the runbook then encodes a path nobody walked. The next responder follows it, it fails, and that is worse than no runbook — it cost them the time they spent trusting it.
The test for a closed loop is unforgiving: when the same failure recurs, does the second responder find the first responder's note without asking anyone? If not, the incident produced a document and no learning.
Doing it
  • Edit the runbook in the same PR as the fix. Reviewers reject a fix whose incident revealed a gap and whose runbook is untouched.
  • Write it inside 48 hours, while the wrong turns are still remembered — the wrong turns are the valuable part.
  • Record the detection path: which alert fired, which should have, and how long before a human saw it.
  • Delete steps that turned out to be unnecessary. A runbook that only grows is unusable within a year.
Embedding it
  • Ask 'which line would have saved you ten minutes?' at the end of every incident. That produces an edit; 'any learnings?' produces a paragraph.
  • Have the responder open the PR, not you, even when you know exactly what it should say.
  • Track one number in the retro: incidents where the runbook was edited, over incidents. Stop tracking it when it is always 100%.
  • Make a second occurrence a team question — why did the note not prevent it? — rather than a personal one.
Defaults
  • Fix and runbook edit in one pull request, always.
  • Written within 48 hours by the person who was paged.
  • Detection path recorded: what fired, what should have, and the delay.
  • Runbooks lose lines as well as gain them.
Gotchas
  • Post-mortems in a separate document space. Written once, read never, and the next responder rediscovers the same missing permission from scratch — the incident repeats in exactly the same shape.
  • Action items with an owner and no date, or a date and no owner. Both mean the item does not exist, and both look like follow-through in a retro.
  • Writing the note a week later. The reconstructed sequence is tidier than the real one, so the runbook documents a path that does not work in the order given.
  • Adding and never removing. Past two pages responders start skimming, the critical line drops below the fold, and the runbook now contains the answer while nobody reads it.

Ownership at handover

Two kinds of ownership, both required, neither substitutable: a group owns the object so it survives a leaver, a named person carries the pager so the alert reaches somebody.

#Named owners, and getting off the rota

What
Production objects are owned by groups in Unity Catalog — an individual owner leaves and takes the ability to grant with them, which the Unity Catalog route covers. Operational ownership is the opposite: it must be a named human, because a group cannot be woken up. The handover artefact holds both.
The ownership sheet — reviewed at handover, then quarterly
AssetUC owner (group)Rota (named)Runbooks (one per alert)Last exercised
dwh_prod.gold.fct_order_linegrp_dwh_engineersdwh-oncall — pool of 4rb_fct_order_line_* — 212.06.2026 game day
Lakeflow pipeline silver_sales_ordergrp_dwh_engineersdwh-oncall — pool of 4rb_silver_sales_order_* — 303.07.2026 incident
Job silver_customergrp_dwh_engineersdwh-oncall — pool of 4rb_silver_customer_* — 1never
dwh_prod.gold.mv_salesgrp_dwh_analyticsbi-oncall — pool of 2rb_mv_sales_* — 1never
The last column is the point of the sheet. Two rows say never — those runbooks are fiction until something exercises them, and the next game day should target exactly those. The pool of two is the second finding: the rota puts two names on call every week, which a pool of two can only sustain until the first holiday.
The handover test is a date, not a document. The consultant comes off the alert rota four weeks before the engagement ends, not on the last day. Those four weeks are the only period in which the team genuinely operates the warehouse while you are still reachable, and every gap they hit is one you close together rather than hear about by email in October.
Why here
The failure has a distinctive signature: an alert destination still pointing at an address nobody reads — the consultant's mailbox, or a shared data-team@ inbox with 400 unread messages. Alerts keep firing and keep being delivered, so every dashboard is green: the alerting works perfectly and lands nowhere.
Nobody notices, because no response to an alert produces no signal at all. It surfaces months later as a freshness problem that had been alerting daily since March.
Doing it
  • Build the ownership sheet in week 20 and review it with names in the room, not with roles.
  • Hold the rota exit date agreed at kick-off, and make it four weeks before the end rather than the last day.
  • Fire a real test alert down every destination at handover and confirm a human replies. Configured is not delivered.
  • Grep the workspace for the consultant's address before the last week, and expect to find it in a job notification nobody remembers creating.
Embedding it
  • Have the team nominate their own primary and backup per asset. Assigned owners are compliance; chosen owners answer the phone.
  • Come off the rota early enough that they get paged while you are still reachable. Reachable and not on the rota is the whole coaching position in one sentence.
  • Make the 'last exercised' column theirs to fill. The rows saying never will bother them more than they bother you.
  • Schedule the first post-handover game day before you leave, with someone else's name on it.
Defaults
  • Groups own UC objects; named people carry the pager. Both, always.
  • A pool of at least four, so the weekly pair of names can always be filled.
  • Consultant off the alert rota four weeks before the end, and reachable for all four.
  • Sheet reviewed quarterly; 'last exercised' is the column that shows rot.
Gotchas
  • The consultant's address left in a job notification. Alerts deliver successfully for months after the contract ends, so nothing looks broken — and the discovery is a freshness breach that had been firing daily since March.
  • A pool of two. It holds until one holiday overlaps one illness, and then the weekly pair cannot be filled, so the page goes to whoever is online — which at 03:00 is nobody.
  • UC objects owned by an individual. They leave, nobody can grant on the table, and the fix needs a metastore admin at exactly the moment the grant was urgent.
  • The ownership sheet living in the handover deck. It is never edited, so within a quarter it names two people who changed team and one who left, and the rota it describes has not existed for months.

Straight to the documentation

The generic platform material lives in the official docs and is not restated here. These are the pages worth having open while you work through this route.

Platform facts on this page verified 29.07.2026 against the official documentation. Volatile claims are anchored to the currency register.