{
  "schema": "leumas.docs.page/1",
  "id": "pkg:@leumas/jobs",
  "slug": "engines/jobs",
  "kind": "capabilities",
  "bucket": "package",
  "title": "@leumas/jobs",
  "name": "@leumas/jobs",
  "eyebrow": null,
  "chip": null,
  "summary": "Leumas Jobs — the one durable, connector-backed job engine every long-running domain shares. Persists each job in the dynamic jobs collection (survives restart), runs kind-keyed runners with a...",
  "keywords": [
    "jobs",
    "resumepending",
    "concurrency",
    "durable",
    "restart",
    "jobs api",
    "leumas jobs",
    "progress"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "# @leumas/jobs\n\nThe **one durable job engine**, and the queue that is Leumas Fabric's control plane. A job is a row in\nthe dynamic `jobs` collection, so it survives a restart; a runner registered for its `kind` does the\nwork; progress rides the automation SSE bus.\n\n```js\nimport { createJobEngine } from '@leumas/jobs';\nimport { createJobsStore, createJobsRouter } from '@leumas/jobs/surface';\n\nconst jobs = createJobEngine({ connector, stream, name: 'default', concurrency: 1 });\njobs.registerRunner('render', async (job, helpers) => {\n  helpers.log('starting');                       // durable — kept on the row, capped\n  await helpers.progress('encoding', 40);\n  return { url: '…' };                           // returning marks it done; throwing marks it failed\n}, { label: 'Render', group: 'Video', memberSafe: true, schedulable: true });\n\napp.use('/api/jobs', createJobsRouter({ store: createJobsStore({ connector }), engines: () => [jobs], gate, isAdmin }));\n```\n\n## The five things worth knowing before you touch it\n\n**1. Submitting is EXECUTION, and it is authorized per kind.** `POST /api/jobs` denies by default: a\nkind is operator-only unless its manifest says `memberSafe: true`. `capability` reaches the whole\necosystem-cell registry, so it deliberately is not. `jobs` is also in `protectedCollections` — the\nrouter is the only write door, because `resumePending()` executes whatever it finds at the next boot.\n\n**2. `waiting` is a state, not a held slot.** A runner that cannot start yet returns\n`helpers.requeue(reason, afterMs)`: the row goes to `waiting` with the reason in words, **the\nconcurrency slot is released**, and it is re-asked later. Blocking instead would deadlock the queue —\nat the default concurrency of 1, one unplaceable job would starve every placeable job behind it.\n`wakeWaiting()` re-asks everything immediately, which is what makes plugging a machine in drain the\nbacklog in front of the person who plugged it in.\n\n**3. Events are ADDRESSED.** `push({ userId: owner, andOperators: true })` — the owner sees their own\njob and operators still see the whole queue. Unaddressed events on that bus reach operators ONLY, so\nfor a while the one person who could not watch a job was the person who started it.\n\n**4. Two engines share this collection.** The default one and the fleet's (`FLEET_CONCURRENCY`), kept\napart only by kind-scoped `resumePending`. Registering the same kind on both makes them fight over the\nsame row. `describeKinds()` reports which engine owns a kind, and at what width.\n\n**5. A row is normalized on read.** `normalizeJob()` backfills `startedAt`/`endedAt` for rows written\nbefore those existed and marks them `derived` — so a surface renders \"≈\" and a percentile can exclude\nthem, rather than reporting a queue latency of zero that never happened.\n\n## Guards\n\n`pnpm smoke:jobs` runs the real engine over an in-memory connector: timestamps, the durable tail, the\n**deadlock canary** (a waiting job must not hold its slot), cancel disarming a pending re-ask, SSE\naddressing, and the four authorization refusals. `pnpm smoke:fabric` covers the fleet path.\n",
  "source": {
    "path": "shared/engines/jobs/README.md",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 3272,
    "hash": "2597b26b42daf837f5b562e9161fbf80829a40e0"
  },
  "urls": {
    "html": "/p/engines/jobs",
    "json": "/docs/engines/jobs.json",
    "md": "/docs/engines/jobs.md"
  },
  "links": {
    "composes": [
      "pkg:@leumas/api-kit"
    ],
    "usedBy": [],
    "product": [],
    "howTo": [],
    "skills": []
  },
  "exports": null
}
