djobs 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. djobs-0.1.0/.github/agents/durable-coder.agent.md +108 -0
  2. djobs-0.1.0/.github/workflows/ci.yml +29 -0
  3. djobs-0.1.0/.github/workflows/publish.yml +28 -0
  4. djobs-0.1.0/.gitignore +41 -0
  5. djobs-0.1.0/.vscode/mcp.json +22 -0
  6. djobs-0.1.0/.vscode/settings.json +3 -0
  7. djobs-0.1.0/CONTRIBUTING.md +48 -0
  8. djobs-0.1.0/LICENSE +21 -0
  9. djobs-0.1.0/PKG-INFO +261 -0
  10. djobs-0.1.0/README.md +235 -0
  11. djobs-0.1.0/docker/docker-compose.yml +21 -0
  12. djobs-0.1.0/docs/ARCHITECTURE.md +360 -0
  13. djobs-0.1.0/docs/HANDOFF.md +246 -0
  14. djobs-0.1.0/docs/IMPLEMENTATION_NOTES.md +388 -0
  15. djobs-0.1.0/docs/ROADMAP.md +385 -0
  16. djobs-0.1.0/examples/demo_workspace/__init__.py +1 -0
  17. djobs-0.1.0/examples/demo_workspace/api/__init__.py +1 -0
  18. djobs-0.1.0/examples/demo_workspace/api/middleware.py +48 -0
  19. djobs-0.1.0/examples/demo_workspace/api/response.py +34 -0
  20. djobs-0.1.0/examples/demo_workspace/api/router.py +34 -0
  21. djobs-0.1.0/examples/demo_workspace/api/serializers.py +46 -0
  22. djobs-0.1.0/examples/demo_workspace/config/__init__.py +1 -0
  23. djobs-0.1.0/examples/demo_workspace/config/database.py +47 -0
  24. djobs-0.1.0/examples/demo_workspace/config/settings.py +23 -0
  25. djobs-0.1.0/examples/demo_workspace/domain/__init__.py +1 -0
  26. djobs-0.1.0/examples/demo_workspace/domain/analytics.py +33 -0
  27. djobs-0.1.0/examples/demo_workspace/domain/discount.py +23 -0
  28. djobs-0.1.0/examples/demo_workspace/domain/pricing.py +31 -0
  29. djobs-0.1.0/examples/demo_workspace/domain/shipping.py +33 -0
  30. djobs-0.1.0/examples/demo_workspace/domain/tax.py +30 -0
  31. djobs-0.1.0/examples/demo_workspace/domain/workflow.py +52 -0
  32. djobs-0.1.0/examples/demo_workspace/models/__init__.py +1 -0
  33. djobs-0.1.0/examples/demo_workspace/models/inventory.py +22 -0
  34. djobs-0.1.0/examples/demo_workspace/models/order.py +25 -0
  35. djobs-0.1.0/examples/demo_workspace/models/payment.py +28 -0
  36. djobs-0.1.0/examples/demo_workspace/models/product.py +18 -0
  37. djobs-0.1.0/examples/demo_workspace/models/user.py +17 -0
  38. djobs-0.1.0/examples/demo_workspace/services/__init__.py +1 -0
  39. djobs-0.1.0/examples/demo_workspace/services/auth.py +28 -0
  40. djobs-0.1.0/examples/demo_workspace/services/cache.py +34 -0
  41. djobs-0.1.0/examples/demo_workspace/services/email.py +24 -0
  42. djobs-0.1.0/examples/demo_workspace/services/notification.py +39 -0
  43. djobs-0.1.0/examples/demo_workspace/services/rate_limiter.py +30 -0
  44. djobs-0.1.0/examples/demo_workspace/utils/__init__.py +1 -0
  45. djobs-0.1.0/examples/demo_workspace/utils/collections.py +34 -0
  46. djobs-0.1.0/examples/demo_workspace/utils/formatters.py +30 -0
  47. djobs-0.1.0/examples/demo_workspace/utils/logging_utils.py +41 -0
  48. djobs-0.1.0/examples/demo_workspace/utils/retry.py +42 -0
  49. djobs-0.1.0/examples/demo_workspace/utils/validators.py +22 -0
  50. djobs-0.1.0/examples/run_ai_demo.py +182 -0
  51. djobs-0.1.0/examples/run_durable_demo.py +233 -0
  52. djobs-0.1.0/examples/run_echo_job.py +42 -0
  53. djobs-0.1.0/examples/run_pool_demo.py +85 -0
  54. djobs-0.1.0/examples/run_retry_job.py +59 -0
  55. djobs-0.1.0/examples/run_scheduler_demo.py +87 -0
  56. djobs-0.1.0/migrations/001_initial.sql +29 -0
  57. djobs-0.1.0/migrations/002_active_idempotency_key.sql +4 -0
  58. djobs-0.1.0/migrations/003_lease_columns.sql +3 -0
  59. djobs-0.1.0/migrations/004_observability_columns.sql +3 -0
  60. djobs-0.1.0/migrations/005_postgres_schema.sql +42 -0
  61. djobs-0.1.0/pyproject.toml +67 -0
  62. djobs-0.1.0/scripts/generate_demo_files.py +934 -0
  63. djobs-0.1.0/src/djobs/__init__.py +49 -0
  64. djobs-0.1.0/src/djobs/api/__init__.py +1 -0
  65. djobs-0.1.0/src/djobs/api/ai_handlers.py +100 -0
  66. djobs-0.1.0/src/djobs/cli.py +160 -0
  67. djobs-0.1.0/src/djobs/core/__init__.py +1 -0
  68. djobs-0.1.0/src/djobs/core/config.py +25 -0
  69. djobs-0.1.0/src/djobs/core/errors.py +35 -0
  70. djobs-0.1.0/src/djobs/core/models.py +45 -0
  71. djobs-0.1.0/src/djobs/core/retry.py +39 -0
  72. djobs-0.1.0/src/djobs/core/states.py +52 -0
  73. djobs-0.1.0/src/djobs/daemon.py +190 -0
  74. djobs-0.1.0/src/djobs/mcp_server.py +508 -0
  75. djobs-0.1.0/src/djobs/observability/__init__.py +6 -0
  76. djobs-0.1.0/src/djobs/observability/inspect.py +47 -0
  77. djobs-0.1.0/src/djobs/observability/logging.py +62 -0
  78. djobs-0.1.0/src/djobs/observability/metrics.py +70 -0
  79. djobs-0.1.0/src/djobs/queue/__init__.py +1 -0
  80. djobs-0.1.0/src/djobs/queue/service.py +173 -0
  81. djobs-0.1.0/src/djobs/scheduler/__init__.py +5 -0
  82. djobs-0.1.0/src/djobs/scheduler/scheduler.py +132 -0
  83. djobs-0.1.0/src/djobs/storage/__init__.py +1 -0
  84. djobs-0.1.0/src/djobs/storage/events.py +20 -0
  85. djobs-0.1.0/src/djobs/storage/postgres.py +631 -0
  86. djobs-0.1.0/src/djobs/storage/sqlite.py +633 -0
  87. djobs-0.1.0/src/djobs/worker/__init__.py +1 -0
  88. djobs-0.1.0/src/djobs/worker/pool.py +200 -0
  89. djobs-0.1.0/src/djobs/worker/registry.py +31 -0
  90. djobs-0.1.0/src/djobs/worker/runner.py +48 -0
  91. djobs-0.1.0/tests/integration/test_ai_platform_flow.py +98 -0
  92. djobs-0.1.0/tests/integration/test_observability_flow.py +81 -0
  93. djobs-0.1.0/tests/integration/test_repository_contract.py +211 -0
  94. djobs-0.1.0/tests/integration/test_scheduler_loop_flow.py +138 -0
  95. djobs-0.1.0/tests/integration/test_sqlite_crash_recovery_flow.py +57 -0
  96. djobs-0.1.0/tests/integration/test_sqlite_job_flow.py +39 -0
  97. djobs-0.1.0/tests/integration/test_sqlite_retry_flow.py +53 -0
  98. djobs-0.1.0/tests/integration/test_worker_pool_flow.py +143 -0
  99. djobs-0.1.0/tests/unit/test_ai_handlers.py +115 -0
  100. djobs-0.1.0/tests/unit/test_concurrency.py +135 -0
  101. djobs-0.1.0/tests/unit/test_config.py +23 -0
  102. djobs-0.1.0/tests/unit/test_daemon.py +324 -0
  103. djobs-0.1.0/tests/unit/test_imports.py +44 -0
  104. djobs-0.1.0/tests/unit/test_job_model.py +25 -0
  105. djobs-0.1.0/tests/unit/test_job_state.py +47 -0
  106. djobs-0.1.0/tests/unit/test_lease.py +137 -0
  107. djobs-0.1.0/tests/unit/test_logging.py +26 -0
  108. djobs-0.1.0/tests/unit/test_mcp_server.py +354 -0
  109. djobs-0.1.0/tests/unit/test_metrics.py +51 -0
  110. djobs-0.1.0/tests/unit/test_observability.py +155 -0
  111. djobs-0.1.0/tests/unit/test_queue_service.py +121 -0
  112. djobs-0.1.0/tests/unit/test_retry_policy.py +46 -0
  113. djobs-0.1.0/tests/unit/test_scheduler.py +257 -0
  114. djobs-0.1.0/tests/unit/test_sqlite_repository.py +160 -0
  115. djobs-0.1.0/tests/unit/test_worker_pool.py +248 -0
  116. djobs-0.1.0/tests/unit/test_worker_registry.py +39 -0
  117. djobs-0.1.0/tests/unit/test_worker_runner.py +134 -0
@@ -0,0 +1,108 @@
1
+ ---
2
+ name: Durable Coder
3
+ description: "Durable Coder — a crash-proof coding agent. Use when you have multi-file tasks like 'add docstrings to all files', 'refactor all tests', 'add type hints everywhere'. Work survives IDE crashes and session interruptions — pick up exactly where you left off."
4
+ tools:
5
+ - djobs/*
6
+ - read
7
+ - edit
8
+ - search
9
+ - execute
10
+ - agent
11
+ ---
12
+
13
+ # Durable Coder Agent
14
+
15
+ You are a crash-proof coding agent. When the user gives you a multi-file task,
16
+ you use a durable job queue to **checkpoint your progress**, so if the session
17
+ crashes or the user closes the chat, the work can be resumed from where it stopped.
18
+
19
+ The user does NOT interact with the queue directly. You manage it transparently.
20
+ **Never ask the user whether to resume — just do it.**
21
+
22
+ ## Core Behavior
23
+
24
+ ### 1. Session Start — Silent Resume
25
+
26
+ At the start of EVERY conversation, call `resume_session` with
27
+ `correlation_id` = the workspace folder path. **Do this silently.**
28
+
29
+ - If incomplete tasks exist AND the user's message is vague (e.g. "hi", "繼續", "continue"):
30
+ → **Automatically resume.** Say "接續上次未完成的工作(N 個檔案),繼續處理。" then start working.
31
+ - If incomplete tasks exist BUT the user gives a NEW specific request:
32
+ → Ignore old tasks, start the new request. Old tasks stay in queue but don't block.
33
+ - If no incomplete tasks → proceed normally with whatever the user asked.
34
+
35
+ **NEVER ask "要繼續嗎?" — that wastes a turn. Just do it.**
36
+
37
+ ### 2. Multi-File Tasks — Checkpoint Automatically
38
+
39
+ When the user asks you to do something across multiple files (>3 files):
40
+
41
+ 1. **Scan** — find all target files.
42
+ 2. **Plan** — tell the user: "找到 N 個檔案,開始處理。" then immediately start.
43
+ 3. **Enqueue** — create one task per file via `enqueue_task` (this is the checkpoint).
44
+ 4. **Execute** — process each task yourself:
45
+ - Read the file.
46
+ - Make the edit.
47
+ - Call `complete_task(task_id)` on success.
48
+ - Call `fail_task(task_id, error)` if the edit fails.
49
+ 5. **Report** — after each batch: "[3/12] ✓ src/djobs/core/models.py"
50
+
51
+ ### 3. Crash Recovery — Seamless Auto-Resume
52
+
53
+ If the session is interrupted and the user opens a new chat:
54
+
55
+ 1. `resume_session` finds unfinished tasks.
56
+ 2. If user's message is vague → auto-resume immediately, no questions asked.
57
+ 3. If user gives a new request → do the new request instead.
58
+
59
+ ### 4. Small Tasks — Just Do It
60
+
61
+ If the task involves ≤3 files, skip the queue and do it directly.
62
+ Don't over-engineer simple requests.
63
+
64
+ ### 5. "What did you do?" — Use audit_log
65
+
66
+ When the user asks about past work — "what changed yesterday?", "did any tasks
67
+ fail?", "summarise today's AI work" — call `audit_log` instead of guessing:
68
+
69
+ - `audit_log()` — 24h summary (task counts, failure list, event breakdown).
70
+ - `audit_log(summary=False, limit=50)` — recent event timeline.
71
+ - `audit_log(event_type="job_failed")` — just the failures.
72
+ - `audit_log(correlation_id=<workspace>)` — scoped to this workspace.
73
+
74
+ This gives accurate answers from the event log, not from memory.
75
+
76
+ ## Rules
77
+
78
+ - **correlation_id**: Always use the workspace folder path.
79
+ - **idempotency_key**: Use `"{task_type}:{file_path}"` to prevent duplicates.
80
+ - **Lifecycle**: After editing each file, call `complete_task(task_id)`. On error, call `fail_task(task_id, error)`. This keeps `resume_session` and `audit_log` accurate.
81
+ - **Progress**: Print `[n/total] ✓ file` after each file completes.
82
+ - **Transparency**: Never ask the user to call queue tools. You are the worker.
83
+
84
+ ## Example
85
+
86
+ ```
87
+ User: "幫所有 Python 檔案加 docstring"
88
+
89
+ Agent:
90
+ 1. resume_session (silently) → no incomplete tasks
91
+ 2. Scan src/ → finds 12 .py files
92
+ 3. "找到 12 個 Python 檔案,開始加 docstring。"
93
+ 4. enqueue all 12 + immediately start processing
94
+ 5. "[1/12] ✓ src/djobs/core/models.py"
95
+ "[2/12] ✓ src/djobs/core/states.py"
96
+ ... (no pauses, no questions) ...
97
+ 6. "全部完成!12/12 檔案已加上 docstring。"
98
+ ```
99
+
100
+ If session crashes after file 7, user opens new chat:
101
+
102
+ ```
103
+ User: "hi"
104
+ Agent:
105
+ 1. resume_session → found 5 incomplete tasks
106
+ 2. "接續上次未完成的 docstring 工作(5/12),繼續處理。"
107
+ 3. Immediately continues from file 8 — no questions asked.
108
+ ```
@@ -0,0 +1,29 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.13"
17
+ - run: pip install ruff
18
+ - run: ruff check src/ tests/
19
+ - run: ruff format --check src/ tests/
20
+
21
+ test:
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - uses: actions/setup-python@v5
26
+ with:
27
+ python-version: "3.13"
28
+ - run: pip install -e ".[dev,mcp]"
29
+ - run: pytest -q --tb=short
@@ -0,0 +1,28 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ environment: pypi
12
+ permissions:
13
+ id-token: write
14
+ contents: read
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.13"
22
+
23
+ - run: python -m pip install --upgrade build twine
24
+
25
+ - run: python -m build
26
+ - run: python -m twine check dist/*
27
+
28
+ - uses: pypa/gh-action-pypi-publish@release/v1
djobs-0.1.0/.gitignore ADDED
@@ -0,0 +1,41 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ *.egg
8
+
9
+ # Virtual environment
10
+ .venv/
11
+
12
+ # SQLite databases
13
+ *.db
14
+ *.sqlite
15
+ *.sqlite3
16
+
17
+ # IDE
18
+ .vscode/*
19
+ !.vscode/mcp.json
20
+ !.vscode/settings.json
21
+
22
+ # OS
23
+ .DS_Store
24
+ Thumbs.db
25
+
26
+ # Environment
27
+ .env
28
+ .env.*
29
+
30
+ # Test / coverage
31
+ .pytest_cache/
32
+ htmlcov/
33
+ .coverage
34
+ coverage.xml
35
+
36
+ # Ruff
37
+ .ruff_cache/
38
+
39
+ # Distribution
40
+ *.whl
41
+ *.tar.gz
@@ -0,0 +1,22 @@
1
+ {
2
+ "servers": {
3
+ "djobs": {
4
+ "type": "stdio",
5
+ "command": "${workspaceFolder}/.venv/Scripts/python.exe",
6
+ "args": ["-m", "djobs.mcp_server"],
7
+ "env": {
8
+ "PYTHONPATH": "${workspaceFolder}/src"
9
+ },
10
+ "autoApprove": [
11
+ "health",
12
+ "resume_session",
13
+ "check_task",
14
+ "complete_task",
15
+ "fail_task",
16
+ "list_tasks",
17
+ "enqueue_task",
18
+ "audit_log"
19
+ ]
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "vscode-nmake-tools.workspaceBuildDirectories": []
3
+ }
@@ -0,0 +1,48 @@
1
+ # Contributing to djobs
2
+
3
+ Thanks for your interest! Here's how to get started.
4
+
5
+ ## Development Setup
6
+
7
+ ```bash
8
+ git clone https://github.com/jhuang-tw/djobs.git
9
+ cd djobs
10
+ python -m venv .venv
11
+ .venv/bin/activate # Windows: .venv\Scripts\activate
12
+ pip install -e ".[dev,mcp]"
13
+ ```
14
+
15
+ ## Running Tests
16
+
17
+ ```bash
18
+ pytest -q # all tests (skips Postgres unless available)
19
+ pytest tests/unit -q # unit tests only
20
+ ruff check src/ tests/ # lint
21
+ ruff format src/ tests/ # auto-format
22
+ ```
23
+
24
+ ## Pull Request Guidelines
25
+
26
+ 1. Fork the repo and create a feature branch from `main`.
27
+ 2. Keep commits focused — one logical change per commit.
28
+ 3. Add or update tests for any new behavior.
29
+ 4. Ensure `pytest -q` and `ruff check` pass before submitting.
30
+ 5. Write a clear PR description explaining *what* and *why*.
31
+
32
+ ## Code Style
33
+
34
+ - Python 3.13+, type hints everywhere.
35
+ - `ruff` for linting and formatting (config in `pyproject.toml`).
36
+ - `src` layout — all package code lives under `src/djobs/`.
37
+ - Tests mirror the source tree: `tests/unit/`, `tests/integration/`.
38
+
39
+ ## Architecture
40
+
41
+ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for module boundaries and design decisions.
42
+
43
+ ## Reporting Issues
44
+
45
+ Open a GitHub issue with:
46
+ - What you expected vs. what happened.
47
+ - Minimal reproduction steps.
48
+ - Python version and OS.
djobs-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 djobs contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
djobs-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,261 @@
1
+ Metadata-Version: 2.4
2
+ Name: djobs
3
+ Version: 0.1.0
4
+ Summary: SQLite-backed durable task queue with first-class MCP integration, purpose-built for AI coding agents (crash recovery, audit trail, embedded daemon).
5
+ Project-URL: Homepage, https://github.com/jhuang-tw/djobs
6
+ Project-URL: Repository, https://github.com/jhuang-tw/djobs
7
+ Project-URL: Issues, https://github.com/jhuang-tw/djobs/issues
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: ai-agent,audit-log,crash-recovery,durable,job-queue,mcp,sqlite,task-queue
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Classifier: Topic :: System :: Distributed Computing
17
+ Requires-Python: >=3.13
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=8.0; extra == 'dev'
20
+ Requires-Dist: ruff>=0.4; extra == 'dev'
21
+ Provides-Extra: mcp
22
+ Requires-Dist: mcp[cli]>=1.0; extra == 'mcp'
23
+ Provides-Extra: pg
24
+ Requires-Dist: psycopg[binary]>=3.1; extra == 'pg'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # djobs
28
+
29
+ **A SQLite-backed durable task queue with first-class MCP integration**, purpose-built for AI coding agents that need crash recovery, audit trails, and zero infrastructure.
30
+
31
+ [![CI](https://github.com/jhuang-tw/djobs/actions/workflows/ci.yml/badge.svg)](https://github.com/jhuang-tw/djobs/actions/workflows/ci.yml)
32
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
33
+ [![Python 3.13+](https://img.shields.io/badge/python-3.13%2B-blue.svg)](https://www.python.org/downloads/)
34
+
35
+ ---
36
+
37
+ ## Why djobs?
38
+
39
+ AI coding agents (GitHub Copilot, Cursor, Cline, etc.) often run multi-file tasks that can take several minutes. When the IDE crashes or the chat disconnects mid-way, in-flight progress is usually lost because the agent's state lives only in chat history.
40
+
41
+ djobs gives agents a small, durable checkpoint queue so they can resume exactly where they stopped:
42
+
43
+ ```
44
+ Agent: "Add docstrings to 12 files"
45
+ -> enqueue 12 tasks (crash-safe checkpoint)
46
+ -> edit file -> complete_task
47
+ -> edit file -> complete_task
48
+ -> ... IDE crashes after file 7 ...
49
+
50
+ New chat: "hi"
51
+ -> resume_session -> 5 incomplete tasks found
52
+ -> auto-resume from file 8 — no questions asked
53
+ ```
54
+
55
+ Under the hood it is a fairly conventional durable job queue (state machine, retry policy, lease, scheduler, event log). The interesting part is how it is wired to AI agents: an MCP server with `enqueue_task` / `complete_task` / `resume_session` / `audit_log`, plus an embedded background daemon, plus a `type_filter` so daemon-managed jobs and agent-managed jobs do not fight over the same queue.
56
+
57
+ ### What is in the box
58
+
59
+ | Area | What you get |
60
+ |------|--------------|
61
+ | **MCP server** | 8 tools exposed via FastMCP / stdio — works in VS Code, Claude Desktop, etc. |
62
+ | **Crash recovery** | `resume_session` returns incomplete tasks for a given workspace / correlation id |
63
+ | **Audit trail** | `audit_log` aggregates `job_events` so you can answer "what did the AI do yesterday?" |
64
+ | **Type isolation** | Built-in daemon only claims job types it has handlers for; AI-only types are left to the agent via `complete_task` / `fail_task` |
65
+ | **SQLite first** | No Redis, RabbitMQ, Docker, or Postgres required for local use |
66
+ | **Postgres path** | Same `JobRepository` protocol implemented on top of `SELECT ... FOR UPDATE SKIP LOCKED` for multi-worker setups |
67
+ | **Test coverage** | 214 passing tests (16 skipped without Postgres), strict ruff lint |
68
+
69
+ ---
70
+
71
+ ## Quick Start
72
+
73
+ ### As a Python Library
74
+
75
+ ```bash
76
+ pip install djobs
77
+ ```
78
+
79
+ ```python
80
+ from djobs import SQLiteJobRepository, QueueService, HandlerRegistry, WorkerPool
81
+
82
+ # 1. Set up
83
+ repo = SQLiteJobRepository.from_path("jobs.db")
84
+ queue = QueueService(repo)
85
+
86
+ # 2. Submit a job
87
+ job = queue.submit("send_email", {"to": "user@example.com"}, max_attempts=3)
88
+
89
+ # 3. Process jobs
90
+ registry = HandlerRegistry()
91
+ registry.register("send_email", lambda payload: send_email(**payload))
92
+
93
+ pool = WorkerPool(queue, registry, worker_id="worker-1", max_concurrent=4)
94
+ pool.run_loop(stop_event)
95
+ ```
96
+
97
+ ### As an MCP Server (for AI Agents)
98
+
99
+ Add to `.vscode/mcp.json`:
100
+
101
+ ```json
102
+ {
103
+ "servers": {
104
+ "djobs": {
105
+ "type": "stdio",
106
+ "command": "python",
107
+ "args": ["-m", "djobs.mcp_server"],
108
+ "autoApprove": [
109
+ "health", "resume_session", "check_task", "complete_task",
110
+ "fail_task", "list_tasks", "enqueue_task", "audit_log"
111
+ ]
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ Then any AI agent can call these MCP tools:
118
+
119
+ | Tool | Purpose |
120
+ |------|---------|
121
+ | `enqueue_task` | Submit a durable task (survives crashes) |
122
+ | `complete_task` | Mark task succeeded after agent finishes work |
123
+ | `fail_task` | Mark task failed with error message |
124
+ | `resume_session` | Find incomplete tasks from previous sessions |
125
+ | `check_task` | Inspect task status, attempts, duration |
126
+ | `list_tasks` | List tasks by correlation_id |
127
+ | `audit_log` | Query event history — "what did the AI do?" |
128
+ | `health` | Queue depth by status |
129
+
130
+ ---
131
+
132
+ ## How is this different from X?
133
+
134
+ djobs is not the first project to expose a task queue to an AI agent over MCP. It targets a specific combination of properties: SQLite-first, MCP-driven, with crash recovery and audit-log style observability built in.
135
+
136
+ | Project | Storage | Focus | Closest to djobs? |
137
+ |---------|---------|-------|-------------------|
138
+ | [TadMSTR/task-queue-mcp](https://github.com/TadMSTR/task-queue-mcp) | YAML files | Multi-agent task hand-off for Claude Code | Closest in spirit. Different storage model (YAML files + dispatcher), no `resume_session` / `audit_log` style observability. |
139
+ | [midweste/mcp-cli-gateway](https://github.com/midweste/mcp-cli-gateway) | SQLite | Routing prompts to CLI agents (Gemini / Codex / Claude) with pacing | Overlaps on persistence + observability, but the unit of work is "dispatch a prompt to a CLI", not "durable user task with retry / lease". |
140
+ | [j0j1j2/claude-tunnel](https://github.com/j0j1j2/claude-tunnel) | In-memory | Pub/sub + 1:1 request/reply + job queue between Claude Code sessions | Different problem: inter-session messaging, not durable work tracking. |
141
+ | Celery / RQ / Dramatiq / Hatchet | Redis / Postgres | General-purpose distributed task queues | Strictly more capable as general queues, but not designed to be driven directly by an AI agent over MCP. |
142
+ | Temporal / Inngest / DBOS | Server / SaaS | Durable workflow / execution engines | Much more powerful and much heavier; no MCP integration; not aimed at single-developer laptop use. |
143
+
144
+ In one sentence: **djobs is what you reach for when you want a small, Celery-shaped Python job queue, driven mostly by an AI agent through MCP, on a single developer machine, with SQLite.**
145
+
146
+ ---
147
+
148
+ ## Architecture
149
+
150
+ ```
151
+ ┌─────────────┐ MCP tools ┌──────────────┐
152
+ │ AI Agent │ ──────────────────> │ MCP Server │
153
+ │ (Copilot) │ <────────────────── │ (FastMCP) │
154
+ └─────────────┘ └──────┬───────┘
155
+
156
+ ┌────────────┼────────────┐
157
+ │ │ │
158
+ ┌─────▼─────┐ ┌────▼────┐ ┌────▼─────┐
159
+ │ Queue │ │ Daemon │ │ Audit │
160
+ │ Service │ │ (Pool + │ │ Log │
161
+ │ │ │ Sched) │ │ │
162
+ └─────┬─────┘ └─────────┘ └──────────┘
163
+
164
+ ┌─────▼─────┐
165
+ │ SQLite │
166
+ │ (or PG) │
167
+ └───────────┘
168
+ ```
169
+
170
+ ### Job State Machine
171
+
172
+ ```
173
+ pending ──────► running ──────► succeeded
174
+ │ │
175
+ │ ├──────► failed
176
+ │ │
177
+ │ ├──────► retry_scheduled ──► pending (retry)
178
+ │ │
179
+ │ └──────► dead_lettered
180
+
181
+ ├──────► succeeded (AI agent direct complete)
182
+ └──────► failed (AI agent direct fail)
183
+ ```
184
+
185
+ ### Module Map
186
+
187
+ | Module | Responsibility |
188
+ |--------|---------------|
189
+ | `djobs.core` | Job model, state machine, domain errors |
190
+ | `djobs.queue` | Submit, claim, complete, fail, retry logic |
191
+ | `djobs.storage` | SQLite & PostgreSQL repositories, event log |
192
+ | `djobs.worker` | Handler registry, WorkerPool, WorkerRunner |
193
+ | `djobs.scheduler` | Retry promotion, expired lease recovery |
194
+ | `djobs.daemon` | Composes WorkerPool + Scheduler into one process |
195
+ | `djobs.observability` | Metrics, structured logging, job inspection |
196
+ | `djobs.mcp_server` | MCP tool definitions, embedded daemon |
197
+ | `djobs.cli` | `djobs serve` CLI entry point |
198
+
199
+ ---
200
+
201
+ ## Examples
202
+
203
+ ```bash
204
+ # Basic job lifecycle
205
+ python examples/run_echo_job.py
206
+
207
+ # Retry with exponential backoff
208
+ python examples/run_retry_job.py
209
+
210
+ # Concurrent worker pool
211
+ python examples/run_pool_demo.py
212
+
213
+ # Scheduler loop (retry promotion + lease recovery)
214
+ python examples/run_scheduler_demo.py
215
+
216
+ # AI task platform (batch submit + cost tracking)
217
+ python examples/run_ai_demo.py
218
+
219
+ # Durable crash recovery demo
220
+ python examples/run_durable_demo.py
221
+ ```
222
+
223
+ ---
224
+
225
+ ## Development
226
+
227
+ ```bash
228
+ git clone https://github.com/jhuang-tw/djobs.git
229
+ cd djobs
230
+ python -m venv .venv && .venv/bin/activate
231
+ pip install -e ".[dev,mcp]"
232
+
233
+ pytest -q # 214 tests (16 skipped without Postgres)
234
+ ruff check src/ tests/ # lint
235
+ ```
236
+
237
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
238
+
239
+ ---
240
+
241
+ ## Roadmap
242
+
243
+ - [x] Durable job queue with retry, lease, heartbeat
244
+ - [x] SQLite + PostgreSQL backends
245
+ - [x] Worker pool with concurrency control
246
+ - [x] Scheduler (retry promotion + lease recovery)
247
+ - [x] Event sourcing & audit trail
248
+ - [x] MCP server with 8 tools
249
+ - [x] Embedded daemon (auto-start with MCP)
250
+ - [x] Type isolation (daemon vs. AI agent tasks)
251
+ - [ ] `pip install djobs` on PyPI
252
+ - [ ] Async worker support
253
+ - [ ] Priority queues
254
+ - [ ] Web dashboard for audit trail
255
+ - [ ] Rate limiting per job type
256
+
257
+ ---
258
+
259
+ ## License
260
+
261
+ [MIT](LICENSE)