# Orphaned Job

> Zero recorded runs across 30 days, on a job at least 30 days old, marks a definition that is dead weight: stale notebooks, pinned libraries, list clutter. ZopNight requires both facts positively: an absent run count or an unknown age abstains, so a young monthly cron is never flagged.

Source: https://zop.dev/integrations/databricks/recommendations/orphaned-job
Updated: 2026-08-19

---

## Dead definitions accumulate silently

An unused job burns no compute, which is exactly why nobody deletes it. The costs are
quieter: a jobs list where the living are indistinguishable from the dead, notebooks
and library versions pinned by a workflow nobody owns, and the occasional heart-stopping
moment when someone re-enables the wrong schedule. This is an orphan-cleanup finding
with a savings figure of 0 by design. The value is a shorter, truthful jobs list, not
a line on the bill.

## Two facts, both required, both positive

The rule fires only when the discoverer's `runCount30d` for the job is present and
exactly 0, and the job's age is known and at least 30 days. The age guard is what
keeps the rule honest: a job created 10 days ago with a monthly or quarterly schedule
legitimately shows zero runs; it has simply never reached its first fire. Age comes
from a pre-derived age field or, failing that, from the job's raw creation timestamp.
When neither the run count nor the age can be established, the rule abstains; an
orphan flag on a guess is worse than no flag.

## The coverage gap, stated plainly

Today only the Azure-side discoverer stamps job age. On GCP workspaces no producer
emits an age field yet, so this rule abstains there entirely rather than false-flag a
young cron for deletion. That asymmetry is a data gap, not a judgment that GCP jobs
cannot be orphaned. Expect findings to appear once the age producer lands.

## Check the run history yourself

```bash
databricks api get '/api/2.1/jobs/runs/list?job_id=123456789&limit=5' | jq '
  .runs[]? | {run_id, start_time, state: .state.result_state}'
```

An empty result over a month, on a job whose `created_time` is older than the month,
is this finding reproduced by hand.

## Pause first, delete second

The remediation is deliberately a two-step hedge. Pause or remove the schedule first,
which is reversible and proves nobody downstream notices. Delete after a quiet interval,
and clean up what the job kept alive: pinned clusters, init scripts, library versions
held only for its benefit. Anything genuinely seasonal deserves a comment in the job
definition saying so, which is what prevents its next appearance on this list.
