stemtrace 0.1.1__tar.gz → 0.2.1__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 (133) hide show
  1. {stemtrace-0.1.1 → stemtrace-0.2.1}/.bumpversion.toml +1 -1
  2. {stemtrace-0.1.1 → stemtrace-0.2.1}/.github/workflows/ci.yml +40 -1
  3. {stemtrace-0.1.1 → stemtrace-0.2.1}/.github/workflows/e2e.yml +1 -0
  4. {stemtrace-0.1.1 → stemtrace-0.2.1}/CHANGELOG.md +20 -3
  5. {stemtrace-0.1.1 → stemtrace-0.2.1}/CONTRIBUTING.md +3 -3
  6. {stemtrace-0.1.1 → stemtrace-0.2.1}/Makefile +2 -2
  7. {stemtrace-0.1.1 → stemtrace-0.2.1}/PKG-INFO +92 -62
  8. {stemtrace-0.1.1 → stemtrace-0.2.1}/README.md +91 -58
  9. stemtrace-0.2.1/docs/screenshots/task_details.png +0 -0
  10. stemtrace-0.2.1/docs/screenshots/unregistered.png +0 -0
  11. stemtrace-0.2.1/docs/screenshots/workflow.png +0 -0
  12. {stemtrace-0.1.1 → stemtrace-0.2.1}/examples/celery_app.py +7 -1
  13. {stemtrace-0.1.1 → stemtrace-0.2.1}/examples/fastapi_integration.py +18 -18
  14. {stemtrace-0.1.1 → stemtrace-0.2.1}/examples/with_auth.py +10 -11
  15. stemtrace-0.2.1/progress.md +5 -0
  16. {stemtrace-0.1.1 → stemtrace-0.2.1}/pyproject.toml +1 -6
  17. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/__init__.py +77 -62
  18. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/core/events.py +67 -1
  19. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/core/graph.py +71 -2
  20. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/core/ports.py +3 -3
  21. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/library/scrubbing.py +1 -1
  22. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/library/signals.py +219 -5
  23. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/library/transports/memory.py +6 -4
  24. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/library/transports/redis.py +51 -7
  25. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/__main__.py +5 -5
  26. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/api/__init__.py +6 -0
  27. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/api/routes.py +214 -11
  28. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/api/schemas.py +43 -0
  29. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/consumer.py +78 -5
  30. stemtrace-0.2.1/src/stemtrace/server/fastapi/extension.py +170 -0
  31. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/fastapi/router.py +12 -3
  32. stemtrace-0.2.1/src/stemtrace/server/store.py +535 -0
  33. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/package-lock.json +83 -23
  34. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/package.json +4 -4
  35. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/api/client.ts +51 -6
  36. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/api/queries.ts +17 -3
  37. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/hooks/WebSocketContext.tsx +18 -4
  38. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/main.tsx +5 -1
  39. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/routes/__root.tsx +6 -0
  40. stemtrace-0.2.1/src/stemtrace/server/ui/frontend/src/routes/registry.tsx +411 -0
  41. stemtrace-0.2.1/src/stemtrace/server/ui/frontend/src/routes/workers.tsx +172 -0
  42. stemtrace-0.2.1/src/stemtrace/server/ui/frontend/src/vite-env.d.ts +10 -0
  43. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/tests/fixtures/mock-api.ts +69 -1
  44. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/tests/fixtures/mock-data.ts +67 -0
  45. stemtrace-0.2.1/src/stemtrace/server/ui/frontend/tests/workers.spec.ts +64 -0
  46. stemtrace-0.2.1/src/stemtrace/server/ui/static.py +122 -0
  47. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/websocket.py +4 -1
  48. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/e2e/conftest.py +1 -1
  49. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/e2e/tasks.py +1 -1
  50. stemtrace-0.2.1/tests/e2e/test_workers_e2e.py +117 -0
  51. stemtrace-0.2.1/tests/integration/test_embedded_static.py +464 -0
  52. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/integration/test_fastapi_integration.py +262 -2
  53. stemtrace-0.2.1/tests/integration/test_static_ui.py +400 -0
  54. stemtrace-0.2.1/tests/integration/test_workers_refresh.py +83 -0
  55. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_bootsteps.py +47 -0
  56. stemtrace-0.2.1/tests/unit/test_consumer.py +436 -0
  57. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_events.py +108 -1
  58. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_examples.py +21 -16
  59. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_graph.py +232 -0
  60. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_init.py +46 -33
  61. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_routes.py +206 -2
  62. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_scrubbing.py +36 -0
  63. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_signals.py +453 -1
  64. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_store.py +721 -7
  65. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_transports.py +113 -1
  66. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_websocket.py +24 -0
  67. {stemtrace-0.1.1 → stemtrace-0.2.1}/uv.lock +2 -17
  68. stemtrace-0.1.1/src/stemtrace/server/fastapi/extension.py +0 -121
  69. stemtrace-0.1.1/src/stemtrace/server/store.py +0 -260
  70. stemtrace-0.1.1/src/stemtrace/server/ui/frontend/src/routes/registry.tsx +0 -211
  71. stemtrace-0.1.1/src/stemtrace/server/ui/static.py +0 -56
  72. stemtrace-0.1.1/tests/integration/test_static_ui.py +0 -114
  73. stemtrace-0.1.1/tests/unit/test_consumer.py +0 -167
  74. {stemtrace-0.1.1 → stemtrace-0.2.1}/.dockerignore +0 -0
  75. {stemtrace-0.1.1 → stemtrace-0.2.1}/.github/dependabot.yml +0 -0
  76. {stemtrace-0.1.1 → stemtrace-0.2.1}/.github/workflows/release.yml +0 -0
  77. {stemtrace-0.1.1 → stemtrace-0.2.1}/.gitignore +0 -0
  78. {stemtrace-0.1.1 → stemtrace-0.2.1}/.pre-commit-config.yaml +0 -0
  79. {stemtrace-0.1.1 → stemtrace-0.2.1}/.python-version +0 -0
  80. {stemtrace-0.1.1 → stemtrace-0.2.1}/Dockerfile +0 -0
  81. {stemtrace-0.1.1 → stemtrace-0.2.1}/Dockerfile.e2e +0 -0
  82. {stemtrace-0.1.1 → stemtrace-0.2.1}/LICENSE +0 -0
  83. {stemtrace-0.1.1 → stemtrace-0.2.1}/build_ui.py +0 -0
  84. {stemtrace-0.1.1 → stemtrace-0.2.1}/docker-compose.e2e.yml +0 -0
  85. {stemtrace-0.1.1 → stemtrace-0.2.1}/docker-compose.yml +0 -0
  86. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/core/__init__.py +0 -0
  87. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/core/exceptions.py +0 -0
  88. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/library/__init__.py +0 -0
  89. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/library/bootsteps.py +0 -0
  90. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/library/config.py +0 -0
  91. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/library/transports/__init__.py +0 -0
  92. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/py.typed +0 -0
  93. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/__init__.py +0 -0
  94. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/api/websocket.py +0 -0
  95. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/fastapi/__init__.py +0 -0
  96. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/fastapi/auth.py +0 -0
  97. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/__init__.py +0 -0
  98. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/biome.json +0 -0
  99. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/index.html +0 -0
  100. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/playwright.config.ts +0 -0
  101. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/postcss.config.js +0 -0
  102. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/components/DateRangePicker.tsx +0 -0
  103. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/components/Filters.tsx +0 -0
  104. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/components/TaskGraph.tsx +0 -0
  105. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/components/TaskList.tsx +0 -0
  106. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/components/TaskStateBadge.tsx +0 -0
  107. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/components/TaskTimeline.tsx +0 -0
  108. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/components/TimelineBar.tsx +0 -0
  109. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/components/Tooltip.tsx +0 -0
  110. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/hooks/useInfiniteScroll.ts +0 -0
  111. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/index.css +0 -0
  112. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/routes/graph.$rootId.tsx +0 -0
  113. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/routes/graphs.tsx +0 -0
  114. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/routes/index.tsx +0 -0
  115. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/routes/tasks.$taskId.tsx +0 -0
  116. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/src/utils/format.ts +0 -0
  117. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/tests/graphs.spec.ts +0 -0
  118. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/tests/registry.spec.ts +0 -0
  119. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/tests/task-detail.spec.ts +0 -0
  120. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/tests/tasks.spec.ts +0 -0
  121. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/tsconfig.json +0 -0
  122. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/tsconfig.tsbuildinfo +0 -0
  123. {stemtrace-0.1.1 → stemtrace-0.2.1}/src/stemtrace/server/ui/frontend/vite.config.ts +0 -0
  124. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/__init__.py +0 -0
  125. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/conftest.py +0 -0
  126. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/e2e/__init__.py +0 -0
  127. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/e2e/test_api_e2e.py +0 -0
  128. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/integration/__init__.py +0 -0
  129. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/integration/test_redis_transport.py +0 -0
  130. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/__init__.py +0 -0
  131. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_auth.py +0 -0
  132. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_config.py +0 -0
  133. {stemtrace-0.1.1 → stemtrace-0.2.1}/tests/unit/test_exceptions.py +0 -0
@@ -1,5 +1,5 @@
1
1
  [tool.bumpversion]
2
- current_version = "0.1.1"
2
+ current_version = "0.2.1"
3
3
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
4
4
  serialize = ["{major}.{minor}.{patch}"]
5
5
  commit = true
@@ -3,6 +3,9 @@ name: CI
3
3
  on:
4
4
  pull_request:
5
5
  branches: [main]
6
+ push:
7
+ branches: [main]
8
+ workflow_dispatch:
6
9
 
7
10
  concurrency:
8
11
  group: ${{ github.workflow }}-${{ github.ref }}
@@ -10,6 +13,7 @@ concurrency:
10
13
 
11
14
  jobs:
12
15
  lint:
16
+ if: github.event_name == 'pull_request'
13
17
  runs-on: ubuntu-latest
14
18
  steps:
15
19
  - uses: actions/checkout@v6
@@ -34,6 +38,7 @@ jobs:
34
38
  run: uv run ruff format --check src/ tests/
35
39
 
36
40
  type-check:
41
+ if: github.event_name == 'pull_request'
37
42
  runs-on: ubuntu-latest
38
43
  steps:
39
44
  - uses: actions/checkout@v6
@@ -55,6 +60,7 @@ jobs:
55
60
  run: uv run mypy src/ --strict
56
61
 
57
62
  test:
63
+ if: github.event_name == 'pull_request'
58
64
  runs-on: ubuntu-latest
59
65
  strategy:
60
66
  matrix:
@@ -76,7 +82,7 @@ jobs:
76
82
  run: uv sync --all-extras
77
83
 
78
84
  - name: Run tests
79
- run: uv run pytest --cov=stemtrace --cov-branch --cov-report=xml --cov-fail-under=80
85
+ run: uv run pytest --cov=stemtrace --cov-branch --cov-report=xml --cov-fail-under=95
80
86
 
81
87
  - name: Upload coverage
82
88
  uses: codecov/codecov-action@v5
@@ -87,6 +93,7 @@ jobs:
87
93
  fail_ci_if_error: false
88
94
 
89
95
  frontend:
96
+ if: github.event_name == 'pull_request'
90
97
  runs-on: ubuntu-latest
91
98
  defaults:
92
99
  run:
@@ -111,6 +118,7 @@ jobs:
111
118
  run: npm run typecheck
112
119
 
113
120
  build:
121
+ if: github.event_name == 'pull_request'
114
122
  runs-on: ubuntu-latest
115
123
  needs: [lint, type-check, test, frontend]
116
124
  steps:
@@ -148,3 +156,34 @@ jobs:
148
156
  with:
149
157
  name: dist
150
158
  path: dist/
159
+
160
+ coverage-main:
161
+ # Keep PR checks as the main gate, but ensure Codecov gets updated for the
162
+ # actual default-branch commit after merges (or direct pushes / releases).
163
+ if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
164
+ runs-on: ubuntu-latest
165
+ steps:
166
+ - uses: actions/checkout@v6
167
+
168
+ - name: Set up Python
169
+ uses: actions/setup-python@v6
170
+ with:
171
+ python-version: "3.12"
172
+
173
+ - name: Install uv
174
+ uses: astral-sh/setup-uv@v7
175
+ with:
176
+ enable-cache: true
177
+
178
+ - name: Install dependencies
179
+ run: uv sync --all-extras
180
+
181
+ - name: Run tests (coverage only)
182
+ run: uv run pytest --cov=stemtrace --cov-branch --cov-report=xml --cov-fail-under=95
183
+
184
+ - name: Upload coverage (main)
185
+ uses: codecov/codecov-action@v5
186
+ with:
187
+ token: ${{ secrets.CODECOV_TOKEN }}
188
+ files: ./coverage.xml
189
+ fail_ci_if_error: false
@@ -126,6 +126,7 @@ jobs:
126
126
  working-directory: src/stemtrace/server/ui/frontend
127
127
  run: npm test
128
128
  env:
129
+ E2E_MODE: real
129
130
  PLAYWRIGHT_BASE_URL: http://localhost:8000
130
131
 
131
132
  - name: Upload Playwright report
@@ -7,7 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [0.1.1] - 2025-12-31
10
+ ## [0.2.1] - 2026-01-03
11
+
12
+ ### Added
13
+ - Task Registry now includes task definition metadata from workers (module, signature, docstring, bound)
14
+
15
+ ### Changed
16
+ - README: updated PyPI badge styling
17
+ - CI: upload Codecov coverage on `main`
18
+
19
+ ## [0.2.0] - 2026-01-03
20
+
21
+ ### Added
22
+ - Workers API endpoints: `GET /api/workers` and `GET /api/workers/{hostname}`
23
+ - Worker lifecycle tracking via `worker_ready` / `worker_shutdown` events to maintain a live worker registry
24
+ - Workers UI page for inspecting online/offline workers and their registered tasks
25
+ - Task Registry now shows which workers registered each task (`registered_by`)
11
26
 
12
27
  ## [0.1.1] - 2025-12-31
13
28
 
@@ -28,7 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
28
43
  - **Core domain models**: `TaskEvent`, `TaskState`, `TaskNode`, `TaskGraph`
29
44
  - **Protocol definitions**: `EventTransport`, `TaskRepository`, `AsyncEventConsumer`
30
45
  - **Event transports**: Redis Streams (`RedisTransport`), in-memory (`MemoryTransport`)
31
- - **Celery signal integration**: Automatic event capture via `stemtrace.init(app)`
46
+ - **Celery signal integration**: Automatic event capture via `stemtrace.init_worker(app)`
32
47
  - **Server components**:
33
48
  - `GraphStore` — Thread-safe in-memory graph storage with LRU eviction
34
49
  - `EventConsumer` / `AsyncEventConsumer` — Background event processing
@@ -44,6 +59,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
44
59
  - **E2E test suite**: Docker API tests + Playwright browser tests
45
60
  - **Comprehensive test suite**: 350+ Python tests, 90%+ coverage
46
61
 
47
- [unreleased]: https://github.com/iansokolskyi/stemtrace/compare/v0.1.1...HEAD
62
+ [unreleased]: https://github.com/iansokolskyi/stemtrace/compare/v0.2.1...HEAD
63
+ [0.2.1]: https://github.com/iansokolskyi/stemtrace/compare/v0.2.0...v0.2.1
64
+ [0.2.0]: https://github.com/iansokolskyi/stemtrace/compare/v0.1.1...v0.2.0
48
65
  [0.1.1]: https://github.com/iansokolskyi/stemtrace/compare/v0.1.0...v0.1.1
49
66
  [0.1.0]: https://github.com/iansokolskyi/stemtrace/releases/tag/v0.1.0
@@ -19,7 +19,7 @@ git clone https://github.com/iansokolskyi/stemtrace.git
19
19
  cd stemtrace
20
20
 
21
21
  # Install dependencies (creates venv automatically)
22
- uv sync --all-extras
22
+ uv sync --extra dev
23
23
 
24
24
  # Install pre-commit hooks
25
25
  uv run pre-commit install
@@ -79,7 +79,7 @@ uv run pytest
79
79
  uv run pytest --cov=stemtrace --cov-report=term-missing
80
80
  ```
81
81
 
82
- **Coverage requirement:** 80% minimum
82
+ **Coverage requirement:** 95% minimum
83
83
 
84
84
  ### Updating Dependencies
85
85
 
@@ -132,7 +132,7 @@ uv lock --upgrade
132
132
  ### Code Quality
133
133
 
134
134
  - Google-style docstrings on all public APIs
135
- - Immutable dataclasses with `frozen=True, slots=True`
135
+ - Pydantic models: immutable with `model_config = ConfigDict(frozen=True)`, mutable with `validate_assignment=True`
136
136
  - Fire-and-forget pattern for publishers (never block)
137
137
  - Fakes over mocks in tests
138
138
 
@@ -36,7 +36,7 @@ format:
36
36
  uv run ruff check --fix src/ tests/
37
37
 
38
38
  test:
39
- uv run pytest --cov=stemtrace --cov-report=term-missing --cov-fail-under=80
39
+ uv run pytest --cov=stemtrace --cov-report=term-missing --cov-fail-under=95
40
40
 
41
41
  # Run tests without coverage (faster iteration)
42
42
  test-fast:
@@ -170,7 +170,7 @@ e2e-mock:
170
170
  e2e-up:
171
171
  docker compose -f docker-compose.e2e.yml up -d --wait
172
172
  @echo "Waiting for services..."
173
- @timeout 60 bash -c 'until curl -sf http://localhost:8000/stemtrace/api/health -o /dev/null; do sleep 2; done' || true
173
+ @timeout 60 bash -c 'until curl -sf http://localhost:8000/stemtrace/api/health -o /dev/null; do sleep 2; done' || (echo "❌ Service failed to become healthy"; exit 1)
174
174
  @echo "✅ E2E environment ready at http://localhost:8000"
175
175
 
176
176
  # Stop E2E test environment
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stemtrace
3
- Version: 0.1.1
3
+ Version: 0.2.1
4
4
  Summary: A lightweight Celery task flow visualizer
5
5
  Project-URL: Homepage, https://github.com/iansokolskyi/stemtrace
6
6
  Project-URL: Documentation, https://github.com/iansokolskyi/stemtrace#readme
@@ -41,7 +41,6 @@ Requires-Dist: build>=1.0.0; extra == 'dev'
41
41
  Requires-Dist: bump-my-version>=0.28.0; extra == 'dev'
42
42
  Requires-Dist: hatchling>=1.20.0; extra == 'dev'
43
43
  Requires-Dist: mypy>=1.0.0; extra == 'dev'
44
- Requires-Dist: pika>=1.3.0; extra == 'dev'
45
44
  Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
46
45
  Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
47
46
  Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
@@ -50,15 +49,13 @@ Requires-Dist: ruff>=0.1.0; extra == 'dev'
50
49
  Requires-Dist: twine>=5.0.0; extra == 'dev'
51
50
  Requires-Dist: types-redis>=4.0.0; extra == 'dev'
52
51
  Requires-Dist: websockets>=12.0; extra == 'dev'
53
- Provides-Extra: rabbitmq
54
- Requires-Dist: pika>=1.3.0; extra == 'rabbitmq'
55
52
  Description-Content-Type: text/markdown
56
53
 
57
54
  # stemtrace 🌿
58
55
 
59
56
  **Zero-infrastructure Celery task flow visualizer**
60
57
 
61
- [![PyPI version](https://badge.fury.io/py/stemtrace.svg)](https://badge.fury.io/py/stemtrace)
58
+ [![PyPI version](https://img.shields.io/pypi/v/stemtrace?color=darklime)](https://pypi.org/project/stemtrace)
62
59
  [![Python](https://img.shields.io/pypi/pyversions/stemtrace.svg)](https://pypi.org/project/stemtrace/)
63
60
  [![CI](https://github.com/iansokolskyi/stemtrace/actions/workflows/ci.yml/badge.svg)](https://github.com/iansokolskyi/stemtrace/actions/workflows/ci.yml)
64
61
  [![codecov](https://codecov.io/gh/iansokolskyi/stemtrace/graph/badge.svg)](https://codecov.io/gh/iansokolskyi/stemtrace)
@@ -72,7 +69,9 @@ Description-Content-Type: text/markdown
72
69
 
73
70
  Ever stared at a failed Celery task wondering "what called this?" or "why did it retry 5 times?"
74
71
 
75
- Stemtrace captures your task executions as a graph — visualize parent→child flows, see retry chains, track groups and chords, all without adding any new infrastructure. Just your existing Redis broker.
72
+ Stemtrace captures your task executions as a graph — visualize parent→child flows, see retry chains, track groups and chords, all without adding any new infrastructure.
73
+
74
+ **Today stemtrace supports Redis** for event transport (via Redis Streams). **RabbitMQ support is planned.**
76
75
 
77
76
  ## ✨ Features
78
77
 
@@ -86,20 +85,58 @@ Stemtrace captures your task executions as a graph — visualize parent→child
86
85
  - **Groups & Chords** — Automatic visualization of `group()` and `chord()` patterns
87
86
  - **Parent-Child Tracking** — See which task spawned which
88
87
 
88
+ **Worker Monitoring & Registry**
89
+ - **Workers page** — See which workers are online/offline and what tasks they have registered
90
+ - **Registry status badges** — Quickly spot tasks that are active, never run, or not registered by any current worker
91
+
89
92
  **Production Ready**
90
93
  - **Zero Infrastructure** — Uses your existing Redis broker, no database needed
91
94
  - **Sensitive Data Scrubbing** — Passwords and API keys filtered automatically
92
95
  - **Read-Only** — Safe for production; never modifies your task queue
93
96
  - **FastAPI Integration** — Mount into your existing app with one line
94
97
 
98
+ ## 🔎 What you’ll see in the dashboard
99
+
100
+ ### Task details (timing, inputs/outputs, errors)
101
+
102
+ - **What you’ll see**: Per-task execution timing (including how long it spent in each state), parameters (args/kwargs), return value, and the full event history.
103
+ - **Why it helps**: Quickly answer “what happened?” for a single task: slow queueing vs slow execution, which retry succeeded, and (on failures) the exception + traceback for debugging.
104
+
105
+ <p align="center">
106
+ <img src="docs/screenshots/task_details.png" width="900" alt="Task detail view showing parameters, result, and timeline" />
107
+ </p>
108
+
109
+ ### Flow graphs (chains, groups, chords)
110
+
111
+ - **What you’ll see**: An interactive DAG of your workflow with parent→child edges, plus clear GROUP/CHORD containers for Celery canvas patterns.
112
+ - **Why it helps**: Understand fan-out/fan-in at a glance (especially chords), spot which branch failed, and debug “why didn’t my callback run?” without grepping logs.
113
+
114
+ <p align="center">
115
+ <img src="docs/screenshots/workflow.png" width="900" alt="Chord visualization in the workflow graph" />
116
+ </p>
117
+
118
+ ### Task registry (registration status + warnings)
119
+
120
+ - **What you’ll see**: A registry of tasks with status badges like **Active**, **Never Run**, and **Not Registered** plus “registered by …” worker info.
121
+ - **Why it helps**: Catch misconfigurations where tasks get stuck in **PENDING** because no current worker has the task registered (common in multi-repo or deploy drift scenarios).
122
+
123
+ <p align="center">
124
+ <img src="docs/screenshots/unregistered.png" width="900" alt="Task registry showing not-registered warning and status badges" />
125
+ </p>
126
+
95
127
  ## 🚀 Quick Start
96
128
 
97
129
  ### 1. Install
98
130
 
99
131
  ```bash
132
+ # Using pip
100
133
  pip install stemtrace
134
+
135
+ # Using uv
136
+ uv add stemtrace
101
137
  ```
102
138
 
139
+
103
140
  ### 2. Instrument your Celery app
104
141
 
105
142
  ```python
@@ -108,8 +145,10 @@ import stemtrace
108
145
 
109
146
  app = Celery("myapp", broker="redis://localhost:6379/0")
110
147
 
111
- # One line to enable flow tracking
112
- stemtrace.init(app)
148
+ # One line to enable event capture.
149
+ # Tip: put this in the module where you define your Celery app so it's imported by
150
+ # both Celery workers and any code that calls app.send_task()/delay().
151
+ stemtrace.init_worker(app)
113
152
  ```
114
153
 
115
154
  ### 3. View the dashboard
@@ -126,11 +165,11 @@ Open [http://localhost:8000](http://localhost:8000).
126
165
 
127
166
  ```python
128
167
  from fastapi import FastAPI
129
- from stemtrace.server import StemtraceExtension
168
+ import stemtrace
130
169
 
131
- flow = StemtraceExtension(broker_url="redis://localhost:6379/0")
132
- app = FastAPI(lifespan=flow.lifespan)
133
- app.include_router(flow.router, prefix="/stemtrace")
170
+ app = FastAPI(lifespan=my_lifespan) # Your existing app
171
+
172
+ stemtrace.init_app(app, broker_url="redis://localhost:6379/0")
134
173
  ```
135
174
 
136
175
  Access at `/stemtrace/` in your existing app — no new services to deploy.
@@ -187,7 +226,7 @@ stemtrace is designed as two decoupled components:
187
226
  ```python
188
227
  import stemtrace
189
228
 
190
- stemtrace.init(
229
+ stemtrace.init_worker(
191
230
  app,
192
231
  # Optional: override broker URL (defaults to Celery's broker_url)
193
232
  transport_url="redis://localhost:6379/0",
@@ -204,9 +243,6 @@ stemtrace.init(
204
243
  safe_keys=frozenset({"public_key"}), # Never scrub these keys
205
244
  )
206
245
 
207
- # Configuration also available via StemtraceConfig model:
208
- # - max_data_size: Maximum serialized data size (default: 10KB)
209
-
210
246
  # Introspection (after init)
211
247
  stemtrace.is_initialized() # -> True
212
248
  stemtrace.get_config() # -> StemtraceConfig
@@ -225,7 +261,7 @@ Scrubbed values appear as `[Filtered]` in the UI.
225
261
 
226
262
  ### Canvas Graph Visualization
227
263
 
228
- stemtrace automatically detects and visualizes Celery canvas constructs:
264
+ `stemtrace` automatically detects and visualizes Celery canvas constructs:
229
265
 
230
266
  ```text
231
267
  # Parent-spawned group: GROUP is child of parent
@@ -261,9 +297,7 @@ batch_processor
261
297
 
262
298
  | Variable | Description | Default |
263
299
  |----------|-------------|---------|
264
- | `STEMTRACE_BROKER_URL` | Broker connection URL | Auto-detect from Celery |
265
- | `STEMTRACE_TTL` | Event TTL in seconds | `86400` |
266
- | `STEMTRACE_PREFIX` | Key/queue prefix | `stemtrace` |
300
+ | `STEMTRACE_BROKER_URL` | Broker URL for `stemtrace server` / `stemtrace consume` and `stemtrace.init_app()` | `redis://localhost:6379/0` |
267
301
 
268
302
  ### Supported Brokers
269
303
 
@@ -271,7 +305,6 @@ batch_processor
271
305
  |--------|------------|--------|
272
306
  | Redis | `redis://`, `rediss://` | ✅ Supported |
273
307
  | RabbitMQ | `amqp://`, `amqps://` | 🚧 Planned |
274
- | Amazon SQS | `sqs://` | 🚧 Planned |
275
308
 
276
309
  ## 🐳 Docker
277
310
 
@@ -295,12 +328,12 @@ services:
295
328
 
296
329
  ## 🖥️ Deployment Options
297
330
 
298
- stemtrace offers two deployment modes depending on your needs:
331
+ `stemtrace` offers two deployment modes depending on your needs:
299
332
 
300
333
  | Mode | Best For | Command |
301
334
  |------|----------|---------|
302
335
  | **Standalone Server** | Dedicated monitoring, simple setup | `stemtrace server` |
303
- | **FastAPI Embedded** | Single-app deployment, existing FastAPI apps | `StemtraceExtension` |
336
+ | **FastAPI Embedded** | Single-app deployment, existing FastAPI apps | `stemtrace.init_app(...)` |
304
337
 
305
338
  ### Option 1: Standalone Server (Recommended)
306
339
 
@@ -326,66 +359,63 @@ stemtrace server \
326
359
 
327
360
  #### High-Scale Production Setup
328
361
 
329
- For high-throughput environments, run the consumer separately from the web server:
330
-
331
- ```bash
332
- # Terminal 1: Run consumer (processes events)
333
- stemtrace consume
334
-
335
- # Terminal 2: Run API server (separate process, shares state via broker)
336
- stemtrace server
337
- ```
362
+ Note: `stemtrace server` includes an embedded consumer today (single-process). A multi-process deployment mode is planned.
338
363
  ### Option 2: FastAPI Embedded
339
364
 
340
365
  Mount stemtrace directly into your existing FastAPI application:
341
366
 
342
367
  ```python
343
368
  from fastapi import FastAPI
344
- from stemtrace.server import StemtraceExtension
369
+ import stemtrace
370
+
371
+ app = FastAPI(lifespan=my_lifespan) # Your existing app with lifespan
372
+
373
+ stemtrace.init_app(app, broker_url="redis://localhost:6379/0") # Wraps lifespan, adds /stemtrace routes
374
+ ```
375
+
376
+ That's it. `init_app()` automatically:
377
+ - Wraps your existing lifespan (Sentry, DB connections, etc. keep working)
378
+ - Mounts the dashboard at `/stemtrace/`
379
+ - Starts the event consumer
380
+
381
+ #### Configuration Options
345
382
 
346
- flow = StemtraceExtension(
383
+ ```python
384
+ import stemtrace
385
+
386
+ # Returns the underlying StemtraceExtension if you need it (optional).
387
+ extension = stemtrace.init_app(
388
+ app,
347
389
  broker_url="redis://localhost:6379/0",
348
- # Optional configuration:
349
- # embedded_consumer=True, # Run consumer in FastAPI process (default)
350
- # serve_ui=True, # Serve React UI (default)
351
- # prefix="stemtrace", # Redis key prefix
352
- # ttl=86400, # Event TTL in seconds
353
- # max_nodes=10000, # Max nodes to keep in memory
390
+ prefix="/stemtrace", # Mount path AND event stream prefix (normalized)
391
+ ttl=86400, # Event TTL in seconds
392
+ max_nodes=10000, # Max nodes in memory
393
+ embedded_consumer=True, # Run consumer in FastAPI process
394
+ serve_ui=True, # Serve React dashboard
395
+ auth_dependency=None, # Optional auth (see below)
354
396
  )
355
- app = FastAPI(lifespan=flow.lifespan)
356
- app.include_router(flow.router, prefix="/stemtrace")
357
397
  ```
358
398
 
359
- Access the dashboard at `/stemtrace/` within your app.
360
-
361
399
  #### With Custom Authentication
362
400
 
363
- Use your existing auth middleware:
364
-
365
401
  ```python
366
402
  from fastapi import Depends
367
- from stemtrace.server import StemtraceExtension
403
+ import stemtrace
368
404
  from your_app.auth import require_admin
369
405
 
370
- flow = StemtraceExtension(
371
- broker_url="redis://localhost:6379/0",
372
- auth_dependency=Depends(require_admin),
373
- )
374
- app = FastAPI(lifespan=flow.lifespan)
375
- app.include_router(flow.router, prefix="/stemtrace")
406
+ stemtrace.init_app(app, broker_url="redis://localhost:6379/0", auth_dependency=Depends(require_admin))
376
407
  ```
377
408
 
378
409
  Or use built-in auth helpers:
379
410
 
380
411
  ```python
381
- from stemtrace.server import StemtraceExtension, require_basic_auth
412
+ import stemtrace
382
413
 
383
- flow = StemtraceExtension(
414
+ stemtrace.init_app(
415
+ app,
384
416
  broker_url="redis://localhost:6379/0",
385
- auth_dependency=require_basic_auth("admin", "secret"),
417
+ auth_dependency=stemtrace.require_basic_auth("admin", "secret"),
386
418
  )
387
- app = FastAPI(lifespan=flow.lifespan)
388
- app.include_router(flow.router, prefix="/stemtrace")
389
419
  ```
390
420
 
391
421
  #### Embedded Consumer Modes
@@ -393,7 +423,7 @@ app.include_router(flow.router, prefix="/stemtrace")
393
423
  | Mode | Use Case | Setup |
394
424
  |------|----------|-------|
395
425
  | Embedded | Development, simple apps | Default — consumer runs in FastAPI process |
396
- | External | Production, high scale | Run `stemtrace consume` separately |
426
+ | External | Production, high scale | Planned |
397
427
 
398
428
  ## 🗺️ Roadmap
399
429
 
@@ -406,12 +436,12 @@ app.include_router(flow.router, prefix="/stemtrace")
406
436
  - ✅ **Sensitive data scrubbing** — Passwords and API keys filtered automatically
407
437
  - ✅ **Real-time updates** — WebSocket-powered live dashboard
408
438
  - ✅ **FastAPI integration** — Mount into your existing app
409
- - ✅ **Task registry** — Browse all discovered task definitions
439
+ - ✅ **Workers page** — Monitor online/offline workers and their registered tasks
440
+ - ✅ **Task registry** — Browse discovered + registered tasks with clear status badges
410
441
 
411
442
  ### Coming Soon
412
443
 
413
444
  - 🔜 **RabbitMQ support** — Use your existing RabbitMQ broker
414
- - 🔜 **Worker monitoring** — See which worker processed each task
415
445
  - 🔜 **Anomaly detection** — Spot stuck, orphaned, or failed tasks
416
446
  - 🔜 **Dashboard with stats** — Success rates, durations, failure trends
417
447
  - 🔜 **OpenTelemetry export** — Send traces to Jaeger, Tempo, Datadog
@@ -427,7 +457,7 @@ See our [Contributing Guide](CONTRIBUTING.md) to get started.
427
457
  ```bash
428
458
  git clone https://github.com/iansokolskyi/stemtrace.git
429
459
  cd stemtrace
430
- uv sync --all-extras # Install dependencies
460
+ uv sync --extra dev # Install dependencies
431
461
  make check # Run tests
432
462
  ```
433
463