Skip to content

feat: Lakeflow Jobs Plugin - #265

Merged
atilafassina merged 2 commits into
mainfrom
ekniazev/jobs-plugin-core
Apr 28, 2026
Merged

feat: Lakeflow Jobs Plugin#265
atilafassina merged 2 commits into
mainfrom
ekniazev/jobs-plugin-core

Conversation

@atilafassina

@atilafassinaatilafassina commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

Important

to maintain contribution history this PR should be rebased when merged
(don't merge it if it has more than 2 commits)

Summary

Resource-scoped jobs plugin following the files plugin pattern. Jobs are configured as named resources discovered from environment variables at startup.

Design

  • Resource-scoped: Only configured jobs are accessible — this is not an open SDK wrapper
  • Env-var discovery: Jobs are discovered from DATABRICKS_JOB_<KEY> env vars (e.g. DATABRICKS_JOB_ETL=123)
  • Single-job shorthand: DATABRICKS_JOB_ID maps to the "default" key
  • Manifest declares job resources with CAN_MANAGE_RUN permission
  • Works with databricks apps init --features jobs

API

// Trigger a configured jobconst{ run_id }=awaitappkit.jobs("etl").runNow();// Trigger and wait for completionconstrun=awaitappkit.jobs("etl").runNowAndWait();// OBO accessawaitappkit.jobs("etl").asUser(req).runNow();// List recent runsconstruns=awaitappkit.jobs("etl").listRuns({limit: 10});// Single-job shorthandawaitappkit.jobs("default").runNow();

Files changed

  • plugins/jobs/manifest.json — declares job resource with CAN_MANAGE_RUN permission
  • plugins/jobs/types.tsJobAPI, JobHandle, JobsExport, IJobsConfig types
  • plugins/jobs/plugin.tsJobsPlugin with discoverJobs(), getResourceRequirements(), resource-scoped createJobAPI()
  • plugins/jobs/index.ts — barrel exports
  • connectors/jobs/client.tslistRuns now respects limit parameter
  • plugins/jobs/tests/plugin.test.ts — 32 tests covering discovery, resource requirements, exports, OBO, multi-job, and auto-fill

Documentation safety checklist

  • Examples use least-privilege permissions
  • Sensitive values are obfuscated
  • No insecure patterns introduced

Reopened from #223 — moved head branch from fork to upstream to fix CI secrets access.

demo

lakeflow-jobs.mp4

CopilotAI review requested due to automatic review settings April 10, 2026 20:40

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new resource-scoped “jobs” plugin to @databricks/appkit, following the existing “files” plugin pattern: jobs are discovered from environment variables at startup and exposed via a keyed accessor API, with HTTP routes for triggering and monitoring runs.

Changes:

  • Introduces plugins/jobs (manifest, defaults, params mapping, types, plugin implementation, and extensive tests).
  • Adds connectors/jobs with telemetry + cancellation support and updates exports/docs to surface the new plugin and types.
  • Updates templates and generated API docs/sidebars to include the new Jobs plugin.

Reviewed changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 7 comments.

Show a summary per file
FileDescription
template/appkit.plugins.jsonAdds “jobs” to plugin template metadata + resource description.
pnpm-lock.yamlLocks new dependency (zod@4.3.6) and related lockfile updates.
packages/appkit/src/plugins/jobs/types.tsPublic types for Jobs plugin API/config (JobAPI/JobHandle/IJobsConfig).
packages/appkit/src/plugins/jobs/plugin.tsCore JobsPlugin: env discovery, dynamic resource requirements, API methods, HTTP routes.
packages/appkit/src/plugins/jobs/params.tsTaskType-based param mapping into SDK request fields.
packages/appkit/src/plugins/jobs/defaults.tsExecution defaults for read/write/stream operations.
packages/appkit/src/plugins/jobs/manifest.jsonPlugin manifest + config schema + baseline resource definition.
packages/appkit/src/plugins/jobs/index.tsBarrel exports for Jobs plugin/types.
packages/appkit/src/plugins/jobs/tests/plugin.test.tsComprehensive unit tests for discovery, API, routes, and validation.
packages/appkit/src/plugins/index.tsExposes jobs from the plugins barrel.
packages/appkit/src/index.tsExposes jobs and related public types/configs from package root.
packages/appkit/src/connectors/jobs/client.tsJobsConnector SDK wrapper with telemetry instrumentation + limit handling.
packages/appkit/src/connectors/jobs/types.tsConnector config type (timeout/telemetry).
packages/appkit/src/connectors/jobs/index.tsConnector barrel exports.
packages/appkit/src/connectors/index.tsExposes jobs connector from connectors barrel.
packages/appkit/package.jsonAdds zod dependency for runtime param schemas + JSON schema generation.
docs/docs/api/appkit/typedoc-sidebar.tsAdds new Jobs docs entries to sidebar.
docs/docs/api/appkit/*.mdAdds generated API docs pages for Jobs plugin types.
docs/docs/api/appkit/index.mdAdds Jobs-related types to API index list.
docs/docs/api/appkit/Interface.BasePluginConfig.mdUpdates “Extended by” list to include IJobsConfig.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadpackages/appkit/src/plugins/jobs/plugin.ts Outdated
Comment threadpackages/appkit/src/plugins/jobs/plugin.ts Outdated
Comment threadpackages/appkit/src/plugins/jobs/plugin.ts Outdated
Comment threadpackages/appkit/src/plugins/jobs/plugin.ts Outdated
Comment threadpackages/appkit/src/plugins/jobs/plugin.ts Outdated
Comment threadpackages/appkit/src/plugins/jobs/types.ts
Comment threadpackages/appkit/src/connectors/jobs/client.ts
@atilafassinaatilafassina changed the title feat: add resource-scoped jobs plugin for Databricks Lakeflow Jobsfeat: Lakeflow Jobs PluginApr 10, 2026
@pkosiec

Copy link
Copy Markdown
Member

How it is different from @keugenek PR here: #221?

If you based on top of Evgenii's work, maybe it's worth to recognize his contribution? What I'm thinking is either:

  • have 2 commits on this PR: Evgenii's base one and yours (with all changes, PR review fixes etc.), and temporarily enable the "Rebase & Merge" option to have 2 commits on main
  • or, merge Evgenii's PR as is, right before this PR (so we'd need to wait for this PR to be approved first)

WDYT?

@atilafassina

atilafassina commented Apr 14, 2026

Copy link
Copy Markdown
ContributorAuthor

@pkosiec

How it is different from @keugenek PR here: #221?
If you based on top of Evgenii's work, maybe it's worth to recognize his contribution? What I'm thinking is either:

  • have 2 commits on this PR: Evgenii's base one and yours (with all changes, PR review fixes etc.), and temporarily enable the "Rebase & Merge" option to have 2 commits on main
  • or, merge Evgenii's PR as is, right before this PR (so we'd need to wait for this PR to be approved first)

WDYT?

I got stuck trying to update his PR because it was opened from a fork, it's noted in the PR description:

Reopened from #223 — moved head branch from fork to upstream to fix CI secrets access.

So, I couldn't get a clean CI to merge it, and if I merged to main, it would trigger a release.
You'll notice that in the commit history the very first commit is his, that's his work that I iterated on top of.

It's also marked as a comment on the original PR

#223 (comment)

@pkosiec

Copy link
Copy Markdown
Member

So, I couldn't get a clean CI to merge it, and if I merged to main, it would trigger a release.

It wouldn't trigger a release currently (finalizing releases is manual) so there shouldn't be a problem with that 👍 but I'd wait until your PR (on top of this one) is ready to merge, and I'd merge both one after another. What do you think?

@atilafassina
atilafassinaforce-pushed the ekniazev/jobs-plugin-core branch from 493146a to 791b146CompareApril 14, 2026 15:17
@atilafassina
atilafassinaforce-pushed the ekniazev/jobs-plugin-core branch 4 times, most recently from 040f7bd to d8133b9CompareApril 14, 2026 19:10

@MarioCadenasMarioCadenas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some comments, but I'm actually wondering if this should just be a "jobs" plugin or if we should extend this and make it support both jobs and pipelines tbh 😅

Comment threadpackages/appkit/src/plugins/jobs/plugin.ts
Comment threadpackages/appkit/src/plugins/jobs/plugin.ts
Comment threadpackages/appkit/src/plugins/jobs/plugin.ts Outdated
Comment threadpackages/appkit/src/plugins/jobs/plugin.ts
Comment threadpackages/appkit/src/plugins/jobs/plugin.ts Outdated
Comment threadpackages/appkit/package.json Outdated
@MarioCadenas

Copy link
Copy Markdown
Collaborator

a few more things I noticed going through the core package:


1. listRuns cache key probably never hits cache

in plugin.ts, listRuns uses options ?? {} in the cache key:

self._readSettings(["jobs:listRuns",jobKey,options??{}])

when options is undefined (which is common), this creates a new {} every time — so if the cache compares by reference, every call is a miss and the 60s TTL does nothing. the other read methods get this right with primitives (["jobs:getRun", jobKey, runId]). can we normalize this to something like ["jobs:listRuns", jobKey, options?.limit ?? "default"]?


2. mapParams silently coerces non-primitives to garbage

in params.ts, the notebook/python_wheel/sql cases do String(v) on every value:

notebook_params: Object.fromEntries(Object.entries(params).map(([k,v])=>[k,String(v)]),),

if someone passes an object or array through (e.g. with a loose zod schema like z.record(z.unknown())), String(v) produces "[object Object]" or "1,2,3" — the job would run with garbage params and no error. can we at least add a guard that rejects non-primitive values here, or coerce more defensively?


3. polling in runAndWait — no backoff or jitter

I know I already asked if we can simplify runAndWait, but specifically the poll interval is fixed at 5s forever. with the default 10min timeout that's 120 API calls per waiter, and if you have multiple SSE clients polling the same run they all hit the Jobs API at the same cadence with no jitter. should we add some basic exponential backoff (e.g. 2s → 5s → 10s → cap at 15s) and a small random jitter so concurrent pollers don't align?

@pkosiecpkosiec left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few comments 👍 Nice work!

Comment thread.claude/skills/install-appkit-tarball.md Outdated
Comment thread.claude/skills/install-appkit-tarball.md Outdated
Comment threadpackages/appkit/src/plugins/jobs/index.ts
@atilafassina
atilafassinaforce-pushed the ekniazev/jobs-plugin-core branch 4 times, most recently from c379239 to b6cd0cfCompareApril 21, 2026 12:52
Jobs are configured as named resources (DATABRICKS_JOB_<KEY> env vars)
and discovered at startup, following the files plugin pattern.
API is scoped to configured jobs:
appkit.jobs('etl').runNow()
appkit.jobs('etl').runNowAndWait()
appkit.jobs('etl').lastRun()
appkit.jobs('etl').listRuns()
appkit.jobs('etl').asUser(req).runNow()
Single-job shorthand via DATABRICKS_JOB_ID env var.
Supports OBO access via asUser(req).
Co-authored-by: Isaac
Signed-off-by: Evgenii Kniazev <evgenii.kniazev@databricks.com>
@atilafassina
atilafassinaforce-pushed the ekniazev/jobs-plugin-core branch from 65bcd39 to e5e2427CompareApril 21, 2026 13:32
@atilafassina

Copy link
Copy Markdown
ContributorAuthor

I added some comments, but I'm actually wondering if this should just be a "jobs" plugin or if we should extend this and make it support both jobs and pipelines tbh 😅

Adding this here just for historical reference.
We synced on this and decided to ship them as separate plugins initially and observe usage first.

@atilafassina
atilafassinaforce-pushed the ekniazev/jobs-plugin-core branch 2 times, most recently from 96c9014 to 7672564CompareApril 22, 2026 14:25
Comment threadpackages/appkit/src/connectors/index.ts
Comment threaddocs/docs/plugins/index.md
@atilafassina
atilafassinaforce-pushed the ekniazev/jobs-plugin-core branch 2 times, most recently from 2956f21 to db91298CompareApril 28, 2026 07:50

@pkosiecpkosiec left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

Works now! Didn't notice CI jobs weren't triggered for the latest run and probably tested an outdated version.

LGTM, as the OBO approach wouldn't be possible currently anyway because of lack of the jobs scope in Apps (unless I missed it)

@atilafassina
atilafassinaforce-pushed the ekniazev/jobs-plugin-core branch from db91298 to 0a4024aCompareApril 28, 2026 08:45
HTTP and SDK surface:
- injectRoutes: POST /:jobKey/run (JSON or SSE via ?stream=true),
GET /:jobKey/runs, GET /:jobKey/runs/:runId, GET /:jobKey/status,
DELETE /:jobKey/runs/:runId
- runAndWait streams JobRunStatus updates with abortable polling,
terminal-state detection, and exponential backoff with jitter
- _parseRunParams eagerly validates request bodies so streaming
requests get a clean 400 instead of a generic SSE error
- Guard against raw params on jobs with neither a schema nor a taskType
- Execution interceptors wired via _readSettings / _writeSettings;
stream defaults for SSE
- Tests covering discovery, validation, routes, error propagation, OBO
Cleanup and polish:
- Mark JobsPlugin export @internal (test-only access)
- Shorten manifest job ID field description; env var is already declared
via the resource field's env key
- Remove install-appkit-tarball skill superseded upstream by
install-appkit-artifact
Playground + template:
- Buffer incomplete SSE lines across reads so data: events split across
chunks are not dropped
- Cap stream log at 500 entries to prevent unbounded growth
- Use counter-based keys on each log entry to avoid React key collisions
Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
@atilafassina
atilafassinaforce-pushed the ekniazev/jobs-plugin-core branch from 0a4024a to 0f67c9fCompareApril 28, 2026 08:57
@atilafassina
atilafassina merged commit 4e92340 into mainApr 28, 2026
8 checks passed
@atilafassina
atilafassina deleted the ekniazev/jobs-plugin-core branch April 28, 2026 09:09
atilafassina added a commit to databricks/databricks-agent-skills that referenced this pull request Apr 28, 2026
Adds a reference for the AppKit Jobs plugin (databricks/appkit#265)
under databricks-apps/references/appkit/jobs.md, plus phase-table and
data-pattern wiring in SKILL.md and overview.md.
Co-authored-by: Isaac
Co-authored-by: Orca <help@stably.ai>
atilafassina added a commit to databricks/databricks-agent-skills that referenced this pull request Apr 28, 2026
Adds a reference for the AppKit Jobs plugin (databricks/appkit#265)
under databricks-apps/references/appkit/jobs.md, plus phase-table and
data-pattern wiring in SKILL.md and overview.md.
Co-authored-by: Isaac
Co-authored-by: Orca <help@stably.ai>
atilafassina added a commit to databricks/databricks-agent-skills that referenced this pull request Apr 28, 2026
Adds a reference for the AppKit Jobs plugin (databricks/appkit#265)
under databricks-apps/references/appkit/jobs.md, plus phase-table and
data-pattern wiring in SKILL.md and overview.md.
Co-authored-by: Isaac
Co-authored-by: Orca <help@stably.ai>
atilafassina added a commit to databricks/databricks-agent-skills that referenced this pull request Apr 28, 2026
Adds a reference for the AppKit Jobs plugin (databricks/appkit#265)
under databricks-apps/references/appkit/jobs.md, plus phase-table and
data-pattern wiring in SKILL.md and overview.md.
Co-authored-by: Isaac
Co-authored-by: Orca <help@stably.ai>
atilafassina added a commit to databricks/databricks-agent-skills that referenced this pull request Apr 28, 2026
## Summary
- Adds `skills/databricks-apps/references/appkit/jobs.md` covering the
AppKit Jobs plugin merged in
[databricks/appkit#265](databricks/appkit#265):
scaffolding, env-based job discovery, plugin/per-job config (deferred to
upstream docs), `JobHandle` API surface, the SP-by-default vs opt-in OBO
gotcha (deliberately the opposite of the Files plugin), HTTP routes,
resource bindings, and troubleshooting.
- Wires the new reference into `skills/databricks-apps/SKILL.md` (phase
table + "When to Use What" bullet) and
`skills/databricks-apps/references/appkit/overview.md` (data-pattern
selector + references table).
- Regenerates `manifest.json` via `python3 scripts/skills.py`.
The reference is intentionally lean (~170 lines) and points to `npx
@databricks/appkit docs Jobs plugin` for type/route signatures, per the
SKILL.md rule that docs are the API authority and skill files cover
anti-patterns and gotchas.
Note: top-level `skills/databricks-jobs/` (Lakeflow Jobs) is a distinct
concept and is unchanged.
## Test plan
- [ ] `python3 scripts/skills.py validate` passes
- [ ] `manifest.json` lists `references/appkit/jobs.md` under
`databricks-apps`
- [ ] Links from `SKILL.md` and `overview.md` resolve to the new
reference
- [ ] Spot-check API claims (handle methods, env-var rules, SP/OBO
defaults) against
[databricks/appkit#265](databricks/appkit#265)
and
[`docs/docs/plugins/jobs.md`](https://github.com/databricks/appkit/blob/main/docs/docs/plugins/jobs.md)
This pull request and its description were written by Isaac.
Made with [Orca](https://github.com/stablyai/orca) 🐋
Co-authored-by: Orca <help@stably.ai>
@MarioCadenasMarioCadenas mentioned this pull request May 12, 2026
4 tasks
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@atilafassina@pkosiec@MarioCadenas@keugenek