Skip to main content

jobwright

CI PyPI Python License: MIT Ruff

An open-source AI layer for governing, validating, and safely shipping data-orchestration jobs with Claude Code.

jobwright treats your jobs — Databricks Jobs, Airflow DAGs, dbt jobs, Snowflake Tasks — as deployable artifacts that deserve a governed lifecycle: a catalog you can recall before you rebuild, architecture-compliance scanning, a per-job validation gate, and a deploy-safety guard that pauses for confirmation before destructive commands — so a stale-definition overwrite can't happen unattended.

Install

jobwright is a Claude Code plugin, and it installs per project — it maps to one repo, one team, one set of jobs. From inside the repo whose jobs it should govern:

claude plugin marketplace add kyle-chalmers/jobwright --scope project
claude plugin install jobwright@jobwright --scope project

That writes the repo's own .claude/settings.json. Commit it, and jobwright travels with the repo:

{
  "extraKnownMarketplaces": {
    "jobwright": {
      "source": { "source": "github", "repo": "kyle-chalmers/jobwright" }
    }
  },
  "enabledPlugins": { "jobwright@jobwright": true }
}

The two claude plugin commands do not set autoUpdate. A fresh jobwright init — or jobwright configure-claude on a repo that already has a config — adds "autoUpdate": true when the key is absent, merging into the entry the CLI wrote rather than treating it as a conflict. An explicit "autoUpdate": false is kept; only configure-claude --force flips it. If your machine already knows a marketplace named jobwright, marketplace add just declares it in this repo's settings ("already on disk — declared in project settings") — expected, not an error. Two things worth knowing about autoUpdate:

  • autoUpdate tracks the plugin's version string, not tags. Bumping the version on main moves everyone on their next session, released or not.
  • Teammates still consent. Trusting the repo registers the marketplace; Claude Code then prompts each person to approve and install the plugin. Committing the file makes jobwright offered to the team, not silently installed on their machines.

Omit --scope project only if you want jobwright for yourself across every repo, in your own ~/.claude/settings.json, rather than for this repo's team.

Requirements: python3 (the hooks are stdlib-only) and uv or pipx, which the plugin uses to run its CLI on demand. Nothing to pip install.

First run

Two files come out of /setup: jobwright.config.yaml, the team's config (commit it), and jobwright.config.local.yaml, yours alone — it holds your CLI profile name and is gitignored, because every machine names its profiles differently. The local file may set only per-user keys; anything else there is rejected so team config cannot drift into a file nobody reviews.

In that repo:

/setup                                       # ≤5 questions → config, catalog, guard active
/start-job JOB-1234 "Daily Revenue Rollup"   # the front door

/setup also offers jobwright gen-readme, a short human-facing README (or README.jobwright.md to merge when one exists) so people, not just agents, can find their way around. /setup detects your platform and pre-fills every answer, so a typical setup is five confirmations. If your job folders sit at the repo root rather than under jobs/, the wizard detects that too and proposes jobs_dir: "."; the catalog (JOBS.md, OBJECTS.md, graph/, objects/) then lands at the root, and the hooks that keep it fresh react only to edits and commits inside job folders (JOB-123_Name/), not to every file in the repo. Set project.graph_notes: false to skip the two graph directories. /start-job then owns the lifecycle — it recalls prior work from the catalog, scaffolds (or resumes) the job, drafts its documentation from the code, gates it with jobwright validate-job, and routes to /safe-deploy when it's ready to ship.

The skills

Skill What it does
/start-job the front door: recall → scaffold or open → document → build → validate → route to deploy
/setup configure a repo (the init wizard) or adopt one that already has jobs
/document-job bring a job's docs up to the gate by inspecting the code — you only answer what the code can't
/safe-deploy the only sanctioned deploy: validates first, diffs live-vs-repo, checks active runs, confirms side-effects
/triage-failure investigate a failed run, classify it, propose a scoped fix
/architecture-audit scan for deprecated-schema references and layer violations (no DB connection)
/build-jobs-index regenerate the deterministic catalog (JOBS.md + OBJECTS.md + Obsidian graph layer; CI-gateable with --check)

What keeps you safe

  • A deploy-safety guard that announces itself at session start and pauses before destructive job/SQL commands — deletes, resets, drops, destructive SQL (even hidden in a -f file). It defends against shell-quote and full-path evasion, and it fails open: it only ever adds a confirmation.
  • A validation gate a deploy can't skip. /safe-deploy runs jobwright validate-job before anything else touches the platform; the same gate runs in /start-job and CI.
  • Drift detection before overwrite. jobwright diff-job compares the live definition to the repo's before a deploy, because repo files go stale — and a stale reset has broken production jobs. On platforms that deploy straight from git it tells you so and points at git diff.
  • Graceful degradation. No platform CLI on PATH? Every file-based check (validation, catalog, compliance scan) still works; doctor names exactly what the live steps need.

Hooks, in full

Trust demands transparency: this plugin runs hooks, so here is every one of them. All are stdlib-only, make no network calls, never write outside the repo, and fail open — a hook error never blocks your session; the guard only ever adds a confirmation.

Event Script What it does
PreToolUse (Bash) hooks/deploy_safety.py Pauses before destructive job/SQL commands (databricks jobs reset/delete, airflow dags delete, dbt prod runs, DROP TASK, destructive SQL incl. -f files / stdin)
PostToolUse (Write|Edit) hooks/regenerate_jobs_index.py Keeps JOBS.md / OBJECTS.md / the graph layer fresh
SessionStart hooks/session_start.sh One-line skills + catalog banner, and announces the guard is active

Every hook is repo-gated on jobwright.config.yaml — zero cost in unrelated repos — and declares an explicit timeout so a hung hook can never stall a session. To turn them all off, disable the plugin (claude plugin disable jobwright). Consumer repos that vendor deploy_safety.py can read the whole file in one screen — that's deliberate.

See it as a graph (Obsidian)

Alongside JOBS.md / OBJECTS.md, jobwright writes a small, auto-maintained graph layer under <jobs_dir>/ — graph/<ticket>.md (a node per job) and objects/<object>.md (a node per data object) — so you can open the repo as an Obsidian vault and browse your jobs. Open a table like ANALYTICS.VW_CUSTOMER and its local graph is every job still on it; open a job and you see the objects it touches plus its deprecated-schema flags. Objects are found by regex — FROM/JOIN/INTO-style refs in SQL and Python SQL strings, plus whole-string DB.SCHEMA.TABLE literals in .py files (the Spark .option("dbtable", ...) shape) — so a name assembled at runtime from variables or an f-string is not indexed. Because objects are the hubs, jobs cluster around the schemas they share — a deprecated schema shows every job that still depends on it, i.e. a live migration map. Point Obsidian at the repo (or <jobs_dir>/), open Graph view, and for the cleanest picture filter -JOBS -OBJECTS -README -AGENTS -CLAUDE and add a color Group on path:objects. It's plain markdown (no plugins, no wikilinks) and renders on GitHub too. On by default; set project.graph_notes: false in jobwright.config.yaml to turn it off (the layer is cleaned up when disabled).

Works with your platform

Databricks Jobs, Snowflake Tasks, Apache Airflow, and dbt ship as adapters; the checks and the guard are platform-agnostic. See examples/ for runnable Databricks and Airflow + BigQuery sample repos, jobwright.config.example.yaml for the full documented config, and docs/architecture.md for the two-seam model, deploy models, and how to add a platform (two files).

Complementary, not overlapping with ticketwright. ticketwright governs ticket-driven analysis work; jobwright governs the jobs themselves — definitions, compliance, and safe deploys. ticketwright's "pause before any prod job deploy" gotcha is exactly the hand-off to jobwright's /safe-deploy. Third kit in the family with streamsnow.

CLI

The plugin runs these for you; you rarely type them. They matter for CI, for scripting, and for repos that use jobwright without Claude Code.

jobwright init [--yes] [--force] | doctor | jobs-index [--check]
          validate-job <folder> [--offline] | diff-job <job>
          check {syntax|job-defs|deps|architecture|docs} <paths>
          new-job <ticket> "<name>" | gen-agents | gen-readme
          configure-claude [--force] | install-precommit [--force]

init --yes accepts the detected proposal without asking anything — for CI, scripts, and agents.

install-precommit is worth running once per repo. The catalog is derived from the job folders, so a job doc that lands without its regenerated catalog leaves the committed catalog stale — and then every worktree branched from that commit inherits the drift, which the PostToolUse rebuild surfaces as phantom uncommitted changes in sessions that never touched those files. The hook regenerates the catalog and stages it into the same commit as the docs that produced it. It is repo-gated, scoped to commits that touch the jobs dir (with jobs_dir: ".", to commits that touch a job folder), and fails open.

Installing without the plugin

The plugin is the primary channel and the one to use with Claude Code — it provisions the CLI itself, so there is nothing to install globally. The PyPI package covers the cases it can't: running the deterministic engines from a shell or in CI, on machines with no Claude Code.

pip install jobwright     # or: uvx jobwright
jobwright jobs-index      # catalog          — no Claude Code needed
jobwright validate-job jobs/JOB-1234_Revenue --offline

Uninstall

To leave no trace, in this order:

claude plugin uninstall jobwright@jobwright --scope project
claude plugin marketplace remove jobwright --scope project

Then delete jobwright.config.yaml and jobwright.config.local.yaml and the generated catalog under <jobs_dir>/ — JOBS.md, OBJECTS.md, index_data.json if present, and the graph/ and objects/ directories. Last, remove the two jobwright keys from .claude/settings.json — extraKnownMarketplaces.jobwright and enabledPlugins."jobwright@jobwright" — or the file itself, if jobwright was all it held.

The order matters. Run the two claude plugin commands before touching .claude/settings.json: if that file is already gone, plugin uninstall has no project-scoped install left to find, and its entry in ~/.claude/plugins/installed_plugins.json stays behind as an orphan.

If you ran jobwright install-precommit, also delete the hook it wrote: pre-commit in the repo's git hooks dir — core.hooksPath if set, otherwise $(git rev-parse --git-common-dir)/hooks, which is .git/hooks/ in the main worktree and the main repo's .git/hooks/ when run from a linked worktree — recognizable by its # jobwright-managed pre-commit v1 marker.

Status

Alpha. Publishing is gated on a security/leak review — see docs/PUBLISHING.md.

MIT licensed.

Metadata

Release files for jobwright 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jobwright 0.4.1
File Size Uploaded
jobwright-0.4.1.tar.gz 131.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jobwright 0.4.1
File Interpreter ABI Platform
jobwright-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 210.9 kB

Release files / jobwright-0.4.1.tar.gz

Download URL jobwright-0.4.1.tar.gz
Size 131.4 kB
Tags Source
SHA-256 checksum
How to use checksums
e7cd5e56d163a0e237bab5e73758f96e47a2c13c723648f7a55bad23c4da66d2
BLAKE2b-256 checksum
How to use checksums
ffabd3a48397872ba2f13c77760a11695ff292033518771583e115ab7ba3f9f0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.

Transparency log

Release files / jobwright-0.4.1-py3-none-any.whl

Download URL jobwright-0.4.1-py3-none-any.whl
Size 79.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7203486e4e578c0f6d1e50d82b94f4fe1f636f9117acccd3e7a9ed9abd4d3aed
BLAKE2b-256 checksum
How to use checksums
4f00c0cbcacf8ddf62f85c25f9722dca205cd9103f761d256083281a962323d6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 2, 2026.

Transparency log

Release history Release notifications | RSS feed

0.5.1

2 release files

This release

0.4.1 This release

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.1.4

2 release files

0.1.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page