{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "background-jobs",
  "type": "registry:block",
  "title": "Background Jobs",
  "description": "Job table, queue abstraction, retry/cancel UI, and worker handoff patterns.",
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [
    "https://stackfoundry.dev/r/drizzle-postgres.json"
  ],
  "files": [
    {
      "path": "packages/db/src/schema/background-jobs.ts",
      "type": "registry:file",
      "target": "packages/db/src/schema/background-jobs.ts",
      "content": "import { integer, jsonb, pgTable, text, timestamp, uuid } from \"drizzle-orm/pg-core\";\n\nexport const backgroundJobs = pgTable(\"background_jobs\", {\n  id: uuid(\"id\").primaryKey().defaultRandom(),\n  name: text(\"name\").notNull(),\n  status: text(\"status\").notNull().default(\"queued\"),\n  payload: jsonb(\"payload\").$type<Record<string, unknown>>().notNull().default({}),\n  attempts: integer(\"attempts\").notNull().default(0),\n  maxAttempts: integer(\"max_attempts\").notNull().default(3),\n  runAfter: timestamp(\"run_after\", { withTimezone: true }).defaultNow().notNull(),\n  lockedAt: timestamp(\"locked_at\", { withTimezone: true }),\n  completedAt: timestamp(\"completed_at\", { withTimezone: true }),\n  failedAt: timestamp(\"failed_at\", { withTimezone: true }),\n  createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull(),\n});\n"
    },
    {
      "path": "apps/web/src/lib/background-jobs.ts",
      "type": "registry:file",
      "target": "apps/web/src/lib/background-jobs.ts",
      "content": "import \"server-only\";\n\nexport type BackgroundJobInput = {\n  name: string;\n  payload?: Record<string, unknown>;\n  maxAttempts?: number;\n  runAfter?: Date;\n};\n\nexport function createBackgroundJob(input: BackgroundJobInput) {\n  return {\n    name: input.name,\n    payload: input.payload ?? {},\n    maxAttempts: input.maxAttempts ?? 3,\n    runAfter: input.runAfter ?? new Date(),\n  };\n}\n\nexport function shouldRetryJob(attempts: number, maxAttempts: number) {\n  return attempts < maxAttempts;\n}\n"
    },
    {
      "path": "apps/web/src/app/(console)/jobs/page.tsx",
      "type": "registry:page",
      "target": "apps/web/src/app/(console)/jobs/page.tsx",
      "content": "const jobs = [\n  { name: \"send_lifecycle_email\", status: \"queued\", attempts: 0 },\n  { name: \"sync_billing_subscription\", status: \"completed\", attempts: 1 },\n];\n\nexport default function JobsPage() {\n  return (\n    <main className=\"flex flex-col gap-6 p-6\">\n      <div>\n        <h1 className=\"text-2xl font-semibold\">Background Jobs</h1>\n        <p className=\"text-muted-foreground\">Track queued work, retries, and worker handoffs.</p>\n      </div>\n      <div className=\"grid gap-3\">\n        {jobs.map((job) => (\n          <div key={job.name} className=\"rounded-lg border p-4\">\n            <p className=\"font-medium\">{job.name}</p>\n            <p className=\"text-sm text-muted-foreground\">{job.status} - {job.attempts} attempts</p>\n          </div>\n        ))}\n      </div>\n    </main>\n  );\n}\n"
    }
  ],
  "maintenanceSkills": [
    {
      "name": "background-jobs",
      "target": ".stackfoundry/skills/background-jobs/SKILL.md",
      "content": "---\nname: background-jobs\ndescription: Maintain the Background Jobs module installed by StackFoundry.\n---\n\n# Background Jobs Maintenance Instructions\n\n- Preserve the module boundary described in `docs.md`.\n- Keep public APIs small and typed.\n- Update tests/checklist.md when behavior changes.\n- Do not introduce secrets, generated machine metadata, or provider lock-in.\n- Keep Drizzle schema exports documented in module.json.\n\n## Shared Skills\n\nWhen provider, framework, or database behavior changes, load the installed shared skill before editing implementation details:\n\n- `.stackfoundry/skills/nextjs/SKILL.md` (source: `registry/skills/nextjs/SKILL.md`)\n- `.stackfoundry/skills/drizzle/SKILL.md` (source: `registry/skills/drizzle/SKILL.md`)\n\nKeep this module skill focused on ownership, installed files, env vars, deployment checks, and module-specific invariants.\n\n"
    },
    {
      "name": "nextjs",
      "target": ".stackfoundry/skills/nextjs/SKILL.md",
      "content": "---\nname: nextjs\ndescription: Maintain Next.js App Router code installed by StackFoundry modules.\n---\n\n# Next.js Operating Instructions\n\n## Installed Location\n\n- Installed target: `.stackfoundry/skills/nextjs/SKILL.md`\n- Registry source: `registry/skills/nextjs/SKILL.md`\n\nAgents maintaining an installed module should load this shared skill from the installed target when provider, framework, database, SDK, or platform behavior is involved. Keep provider-specific API details here instead of duplicating them inside module maintenance skills.\n\n- Keep server-only data access out of Client Components.\n- Put route handlers under `app/api` and UI routes under the relevant App Router segment.\n- Prefer Server Components for data loading and add `\"use client\"` only for interactivity.\n- Keep public environment variables prefixed with `NEXT_PUBLIC_`; keep secrets server-only.\n- Re-run typecheck and build after changing route handlers, layouts, or shared app configuration.\n"
    },
    {
      "name": "drizzle",
      "target": ".stackfoundry/skills/drizzle/SKILL.md",
      "content": "---\nname: drizzle\ndescription: Maintain Drizzle ORM and Postgres code installed by StackFoundry modules.\n---\n\n# Drizzle Operating Instructions\n\n## Installed Location\n\n- Installed target: `.stackfoundry/skills/drizzle/SKILL.md`\n- Registry source: `registry/skills/drizzle/SKILL.md`\n\nAgents maintaining an installed module should load this shared skill from the installed target when provider, framework, database, SDK, or platform behavior is involved. Keep provider-specific API details here instead of duplicating them inside module maintenance skills.\n\n- Keep database access in server-only code.\n- Add schema changes under `packages/db/src/schema` and export shared tables from the schema barrel.\n- Generate and commit migrations when schema changes are intended.\n- Use typed query helpers instead of raw SQL unless the query needs a documented escape hatch.\n- Include tenant, organization, or user scope in queries and cache tags whenever data is not global.\n"
    }
  ],
  "envVars": {},
  "docs": "# Background Jobs Module\n\nJob table, queue abstraction, retry/cancel UI, and worker handoff patterns.\n\n## Owns\n\n- `packages/db/src/schema/background-jobs.ts`\n- `apps/web/src/lib/background-jobs.ts`\n- `apps/web/src/app/(console)/jobs/page.tsx`\n\n## Environment\n\nRequires `DATABASE_URL` through the `drizzle-postgres` dependency.\n\n## Maintenance\n\n- Keep this module provider-neutral unless a provider adapter is added as a separate module.\n- Update the manifest when source files, schema exports, dependencies, or environment variables change.\n- Verify install output with `stackfoundry add background-jobs --target <app> --dry-run` before promoting status.\n",
  "meta": {
    "category": "operations",
    "env": [],
    "status": "ready",
    "maturity": "ready",
    "drizzle": {
      "schemaExports": [
        "backgroundJobs"
      ],
      "migrationRecommended": true
    },
    "recommendedFor": []
  }
}
