flowsense-engine 0.2.1__tar.gz → 0.4.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 (125) hide show
  1. flowsense_engine-0.4.0/CHANGELOG.md +107 -0
  2. flowsense_engine-0.4.0/MANIFEST.in +2 -0
  3. {flowsense_engine-0.2.1/src/flowsense_engine.egg-info → flowsense_engine-0.4.0}/PKG-INFO +61 -11
  4. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/README.md +60 -10
  5. flowsense_engine-0.4.0/docs/alerting.md +57 -0
  6. flowsense_engine-0.4.0/docs/architecture.md +138 -0
  7. flowsense_engine-0.4.0/docs/authentication.md +77 -0
  8. flowsense_engine-0.4.0/docs/benchmarks.md +37 -0
  9. flowsense_engine-0.4.0/docs/grafana.md +66 -0
  10. flowsense_engine-0.4.0/docs/migrating-to-0.3.md +66 -0
  11. flowsense_engine-0.4.0/docs/prometheus.md +58 -0
  12. flowsense_engine-0.4.0/docs/python-api.md +142 -0
  13. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/docs/releasing.md +6 -2
  14. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/pyproject.toml +4 -3
  15. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/__init__.py +26 -0
  16. flowsense_engine-0.4.0/src/flowsense/application/__init__.py +52 -0
  17. flowsense_engine-0.4.0/src/flowsense/application/analysis_engine.py +71 -0
  18. flowsense_engine-0.4.0/src/flowsense/application/analyzer.py +21 -0
  19. flowsense_engine-0.4.0/src/flowsense/application/batch.py +129 -0
  20. flowsense_engine-0.4.0/src/flowsense/application/client.py +61 -0
  21. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/application/output.py +17 -0
  22. flowsense_engine-0.4.0/src/flowsense/application/ports.py +43 -0
  23. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/application/serialization.py +35 -0
  24. flowsense_engine-0.4.0/src/flowsense/application/use_cases.py +36 -0
  25. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/cli/main.py +41 -11
  26. flowsense_engine-0.4.0/src/flowsense/collector/__init__.py +9 -0
  27. flowsense_engine-0.4.0/src/flowsense/config.py +22 -0
  28. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/domain/__init__.py +2 -0
  29. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/domain/exceptions.py +6 -0
  30. flowsense_engine-0.4.0/src/flowsense/domain/models.py +28 -0
  31. flowsense_engine-0.4.0/src/flowsense/engine/analyzer.py +23 -0
  32. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/engine/change_point.py +3 -0
  33. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/engine/drift.py +3 -0
  34. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/engine/root_cause.py +3 -1
  35. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/engine/trend.py +3 -0
  36. flowsense_engine-0.4.0/src/flowsense/engine/validation.py +12 -0
  37. flowsense_engine-0.4.0/src/flowsense/infrastructure/airflow/__init__.py +34 -0
  38. flowsense_engine-0.4.0/src/flowsense/infrastructure/airflow/auth.py +63 -0
  39. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/infrastructure/airflow/client.py +64 -50
  40. {flowsense_engine-0.2.1/src/flowsense → flowsense_engine-0.4.0/src/flowsense/infrastructure/airflow}/config.py +26 -9
  41. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/infrastructure/airflow/dto.py +9 -1
  42. flowsense_engine-0.4.0/src/flowsense/infrastructure/airflow/factory.py +35 -0
  43. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/mcp/server.py +3 -11
  44. flowsense_engine-0.4.0/src/flowsense/models/__init__.py +17 -0
  45. flowsense_engine-0.4.0/src/flowsense/observability/__init__.py +5 -0
  46. flowsense_engine-0.4.0/src/flowsense/observability/prometheus.py +248 -0
  47. flowsense_engine-0.4.0/src/flowsense/observability/service.py +90 -0
  48. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0/src/flowsense_engine.egg-info}/PKG-INFO +61 -11
  49. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense_engine.egg-info/SOURCES.txt +32 -0
  50. flowsense_engine-0.4.0/tests/test_airflow_auth.py +114 -0
  51. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_airflow_client.py +22 -31
  52. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_airflow_client_integration.py +2 -2
  53. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_airflow_compatibility.py +86 -11
  54. flowsense_engine-0.4.0/tests/test_airflow_contract.py +95 -0
  55. flowsense_engine-0.4.0/tests/test_airflow_factory.py +76 -0
  56. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_airflow_resilience.py +7 -8
  57. flowsense_engine-0.4.0/tests/test_analysis_golden.py +111 -0
  58. flowsense_engine-0.4.0/tests/test_analysis_use_case.py +68 -0
  59. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_analyzer.py +1 -2
  60. flowsense_engine-0.4.0/tests/test_architecture.py +78 -0
  61. flowsense_engine-0.4.0/tests/test_batch_analysis_output.py +71 -0
  62. flowsense_engine-0.4.0/tests/test_benchmark_analysis.py +56 -0
  63. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_cli.py +69 -31
  64. flowsense_engine-0.4.0/tests/test_deprecations.py +26 -0
  65. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_domain.py +39 -9
  66. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_history.py +1 -1
  67. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_impact.py +1 -1
  68. flowsense_engine-0.4.0/tests/test_library_client.py +169 -0
  69. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_mcp_server.py +15 -13
  70. flowsense_engine-0.4.0/tests/test_observability_assets.py +58 -0
  71. flowsense_engine-0.4.0/tests/test_observation_validation.py +27 -0
  72. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_package_metadata.py +1 -1
  73. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_policy.py +1 -2
  74. flowsense_engine-0.4.0/tests/test_prometheus.py +152 -0
  75. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_propagation.py +1 -1
  76. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_public_api.py +36 -0
  77. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_root_cause.py +22 -3
  78. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_timing.py +1 -2
  79. flowsense_engine-0.2.1/CHANGELOG.md +0 -48
  80. flowsense_engine-0.2.1/MANIFEST.in +0 -2
  81. flowsense_engine-0.2.1/src/flowsense/application/__init__.py +0 -20
  82. flowsense_engine-0.2.1/src/flowsense/application/analyzer.py +0 -68
  83. flowsense_engine-0.2.1/src/flowsense/application/ports.py +0 -11
  84. flowsense_engine-0.2.1/src/flowsense/domain/models.py +0 -19
  85. flowsense_engine-0.2.1/src/flowsense/engine/analyzer.py +0 -17
  86. flowsense_engine-0.2.1/src/flowsense/infrastructure/airflow/__init__.py +0 -13
  87. flowsense_engine-0.2.1/src/flowsense/mcp/__init__.py +0 -0
  88. flowsense_engine-0.2.1/src/flowsense/models/__init__.py +0 -7
  89. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/LICENSE +0 -0
  90. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/setup.cfg +0 -0
  91. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/application/pipeline.py +0 -0
  92. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/application/request.py +0 -0
  93. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/cli/__init__.py +0 -0
  94. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/cli/report.py +0 -0
  95. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/collector/airflow_client.py +0 -0
  96. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/domain/enums.py +0 -0
  97. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/domain/policy.py +0 -0
  98. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/domain/results.py +0 -0
  99. {flowsense_engine-0.2.1/src/flowsense/collector → flowsense_engine-0.4.0/src/flowsense/engine}/__init__.py +0 -0
  100. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/engine/history.py +0 -0
  101. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/engine/impact.py +0 -0
  102. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/engine/propagation.py +0 -0
  103. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/engine/timing.py +0 -0
  104. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/infrastructure/__init__.py +0 -0
  105. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/infrastructure/airflow/exceptions.py +0 -0
  106. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/infrastructure/airflow/mapper.py +0 -0
  107. {flowsense_engine-0.2.1/src/flowsense/engine → flowsense_engine-0.4.0/src/flowsense/mcp}/__init__.py +0 -0
  108. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/models/dag_analysis.py +0 -0
  109. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/models/task_run.py +0 -0
  110. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/py.typed +0 -0
  111. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense/version.py +0 -0
  112. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense_engine.egg-info/dependency_links.txt +0 -0
  113. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense_engine.egg-info/entry_points.txt +0 -0
  114. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense_engine.egg-info/requires.txt +0 -0
  115. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/src/flowsense_engine.egg-info/top_level.txt +0 -0
  116. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_airflow_mapper.py +0 -0
  117. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_analysis_output.py +0 -0
  118. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_analysis_pipeline.py +0 -0
  119. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_analysis_request.py +0 -0
  120. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_change_point.py +0 -0
  121. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_cli_report.py +0 -0
  122. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_dag_summary.py +0 -0
  123. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_drift.py +0 -0
  124. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_mcp_integration.py +0 -0
  125. {flowsense_engine-0.2.1 → flowsense_engine-0.4.0}/tests/test_trend.py +0 -0
@@ -0,0 +1,107 @@
1
+ # Changelog
2
+
3
+ All notable changes to FlowSense are documented in this file. The project uses
4
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
+
6
+ ## [Unreleased]
7
+
8
+ ## [0.4.0] - 2026-09-20
9
+
10
+ ### Added
11
+
12
+ - Added paginated Airflow 2 and 3 DAG discovery with optional paused-DAG
13
+ inclusion.
14
+ - Added an immutable multi-DAG analysis result and bounded, opt-in concurrency
15
+ through `FlowSenseClient.analyze_many()`.
16
+ - Added a versioned, typed batch output document with serialization and JSON
17
+ Schema helpers.
18
+ - Added the `FlowSenseClient` facade for concise embedded Python usage and an
19
+ explicit Airflow data-source factory for application-owned configuration.
20
+ - Added pluggable Airflow authentication providers for Basic Auth, login-token
21
+ exchange, static or rotating bearer tokens, and deployment-specific headers.
22
+ - Added a bounded Prometheus metrics exporter and the optional
23
+ `flowsense serve-metrics` command for continuous DAG analysis.
24
+ - Added a provisioned Grafana dashboard and local Docker Compose example for
25
+ inspecting analysis health, coverage, severity, task drift, and handoffs.
26
+ - Added Prometheus alert rules, Alertmanager configuration, synthetic rule
27
+ tests, and CI validation for FlowSense analysis failures and anomalies.
28
+
29
+ ### Changed
30
+
31
+ - Kept observability integrations as optional delivery adapters so the core
32
+ analysis package remains independent from Prometheus, Grafana, and hosted
33
+ service infrastructure.
34
+
35
+ ## [0.3.0] - 2026-09-20
36
+
37
+ ### Added
38
+
39
+ - Added `AnalyzeDAG` as the shared application use-case boundary for CLI, MCP,
40
+ and library integrations.
41
+ - Added the `DAGAnalysisEngine` extension port and injectable default analysis
42
+ engine.
43
+ - Added versioned golden regression coverage for the complete analysis output.
44
+ - Added deterministic performance benchmarks with machine-readable results and
45
+ a manually triggered GitHub Actions workflow.
46
+ - Added an Airflow 2 and Airflow 3 REST contract test matrix covering
47
+ authentication, routing, validation, mapping, and end-to-end analysis.
48
+
49
+ ### Changed
50
+
51
+ - Separated environment loading and Airflow client construction through explicit
52
+ `AirflowConfig` and infrastructure factories.
53
+ - Made the `TaskRun` domain entity immutable and framework-independent while
54
+ keeping Pydantic at external DTO and output-contract boundaries.
55
+ - Added automated enforcement preventing the domain layer from importing
56
+ Pydantic.
57
+ - Added architecture dependency guardrails and documented supported extension
58
+ points and compatibility boundaries.
59
+
60
+ ### Fixed
61
+
62
+ - Rejected non-finite and negative analysis inputs at the domain boundary.
63
+ - Made root-cause selection deterministic when candidates have equal scores.
64
+
65
+ ## [0.2.1] - 2026-09-10
66
+
67
+ ### Changed
68
+
69
+ - Renamed the Python distribution to `flowsense-engine` while preserving the
70
+ `flowsense` import package and CLI command.
71
+ - Added tokenless PyPI publishing through GitHub Actions Trusted Publishing.
72
+
73
+ ## [0.2.0] - 2026-09-10
74
+
75
+ ### Added
76
+
77
+ - Task handoff drift, impact classification, propagation, and root-cause analysis.
78
+ - Configurable drift, mapped-task aggregation, change-point, and trend policies.
79
+ - Apache Airflow 2 and 3 API compatibility with pagination and bounded history.
80
+ - Retry, timeout, backoff, and `Retry-After` handling for Airflow requests.
81
+ - Historical DAG run analysis with preceding-history isolation.
82
+ - Rich CLI reports, JSON output, severity-based exit thresholds, and schema output.
83
+ - MCP analysis tool with the same policy and versioned response contract as the CLI.
84
+ - Typed `AnalysisRequest` and `AnalysisDocument` contracts with JSON Schema support.
85
+ - Supported top-level library API, typed package marker, and Python 3.12/3.13 CI.
86
+
87
+ ### Changed
88
+
89
+ - Split the analysis workflow into domain, application, infrastructure, and adapter
90
+ boundaries.
91
+ - Analysis output schema advanced to version `1.1` with `current_dag_run_id`.
92
+ - Expected configuration, Airflow API, and payload failures now use a unified error
93
+ hierarchy.
94
+
95
+ ### Fixed
96
+
97
+ - Preserved anomaly origins across normal dependency gaps.
98
+ - Detected drift when the historical median absolute deviation is zero.
99
+ - Aggregated dynamically mapped task instances before analysis.
100
+ - Reported insufficient history and invalid handoff timing as diagnostics.
101
+
102
+ ## [0.1.0] - 2026-08-18
103
+
104
+ ### Added
105
+
106
+ - Initial FlowSense prototype with Airflow task-duration collection and robust
107
+ median/MAD drift detection.
@@ -0,0 +1,2 @@
1
+ include CHANGELOG.md
2
+ recursive-include docs *.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flowsense-engine
3
- Version: 0.2.1
3
+ Version: 0.4.0
4
4
  Summary: Temporal drift and anomaly detection for Apache Airflow
5
5
  Author: Omer Cengiz
6
6
  License-Expression: Apache-2.0
@@ -37,8 +37,11 @@ Dynamic: license-file
37
37
 
38
38
  Temporal drift, anomaly detection, and dependency-aware propagation analysis for Apache Airflow.
39
39
 
40
- Current package release: `0.2.1`. See [CHANGELOG.md](CHANGELOG.md) for release
41
- notes and [docs/releasing.md](docs/releasing.md) for the release checklist.
40
+ Current package release: `0.4.0`. See [CHANGELOG.md](CHANGELOG.md) for release
41
+ notes, [docs/architecture.md](docs/architecture.md) for the system architecture,
42
+ [docs/benchmarks.md](docs/benchmarks.md) for performance measurement, and
43
+ [docs/migrating-to-0.3.md](docs/migrating-to-0.3.md) for migration guidance.
44
+ Maintainers can use [docs/releasing.md](docs/releasing.md) as the release checklist.
42
45
 
43
46
  FlowSense analyzes historical DAG executions to identify abnormal task behavior and trace how anomalies propagate through downstream dependencies.
44
47
 
@@ -177,6 +180,7 @@ AIRFLOW_USERNAME=your_username
177
180
  AIRFLOW_PASSWORD=your_password
178
181
  AIRFLOW_API_VERSION=v2
179
182
  AIRFLOW_AUTH_MODE=token
183
+ # AIRFLOW_BEARER_TOKEN=your_static_token
180
184
  AIRFLOW_CONNECT_TIMEOUT=10
181
185
  AIRFLOW_READ_TIMEOUT=10
182
186
  AIRFLOW_MAX_RETRIES=2
@@ -189,6 +193,16 @@ Stable REST API deployments. Airflow 3.x uses the `v2` API and typically uses
189
193
  token authentication. Authentication still depends on the API auth backend
190
194
  configured in the Airflow deployment.
191
195
 
196
+ Static bearer tokens can use `AIRFLOW_AUTH_MODE=bearer` with
197
+ `AIRFLOW_BEARER_TOKEN`; username and password are not required in that mode.
198
+ Library integrations may inject a custom `AirflowAuthProvider` for rotating
199
+ tokens, identity-aware proxies, or deployment-specific headers. See
200
+ [docs/authentication.md](docs/authentication.md) for the complete contract.
201
+
202
+ CI verifies both integrations through versioned Airflow 2 and Airflow 3 REST
203
+ contract fixtures. These boundary tests cover authentication, endpoint routing,
204
+ response validation, domain mapping, and the complete analysis use case.
205
+
192
206
  Transient transport failures and HTTP `429`, `502`, `503`, and `504` responses
193
207
  are retried with exponential backoff. `Retry-After` is honored when Airflow
194
208
  provides it. Connect/read timeouts, retry count, and base backoff can be tuned
@@ -272,22 +286,41 @@ tooling, or client code generation.
272
286
 
273
287
  ## Library API
274
288
 
275
- FlowSense can also be used as a Python library through its supported top-level
276
- API:
289
+ `FlowSenseClient` is the recommended in-process Python API. The environment
290
+ configured Airflow factory keeps the basic setup concise:
277
291
 
278
292
  ```python
279
- from flowsense import AirflowClient, analyze_dag
293
+ from flowsense import FlowSenseClient
294
+ from flowsense.infrastructure.airflow import create_airflow_data_source
280
295
 
281
- with AirflowClient() as source:
282
- analysis = analyze_dag(
283
- dag_id="flowsense_demo",
284
- source=source,
285
- )
296
+ client = FlowSenseClient(create_airflow_data_source)
297
+ analysis = client.analyze("flowsense_demo")
286
298
 
287
299
  print(analysis.overall_severity)
288
300
  print(analysis.primary_origin)
289
301
  ```
290
302
 
303
+ Applications that own configuration can combine `AirflowConfig` with
304
+ `create_airflow_data_source_factory`. Typed integrations may continue to call
305
+ `client.execute(AnalysisRequest(...))`. See the complete
306
+ [Python API guide](docs/python-api.md).
307
+
308
+ Multiple DAGs can be analyzed with bounded, opt-in concurrency. Expected
309
+ FlowSense failures are isolated per DAG:
310
+
311
+ ```python
312
+ result = client.analyze_many(
313
+ ["orders", "payments", "inventory"],
314
+ max_concurrency=3,
315
+ )
316
+ ```
317
+
318
+ `AirflowClient.list_dag_ids()` provides paginated DAG discovery and excludes
319
+ paused DAGs by default.
320
+
321
+ `serialize_batch_analysis(result)` converts the result into the stable batch
322
+ output contract without exposing raw exception objects.
323
+
291
324
  Analysis behavior can be customized with an immutable policy:
292
325
 
293
326
  ```python
@@ -345,6 +378,23 @@ The server exposes the `analyze_airflow_dag` tool, which returns task drift,
345
378
  handoff drift, impact classification, propagation paths, and primary root-cause
346
379
  information for a DAG.
347
380
 
381
+ ## Prometheus
382
+
383
+ Run continuous analysis and expose the latest DAG and bounded task-level metrics:
384
+
385
+ ```bash
386
+ flowsense serve-metrics example_dag --host 0.0.0.0 --port 9108
387
+ ```
388
+
389
+ See [docs/prometheus.md](docs/prometheus.md) for the metric contract,
390
+ cardinality policy, and Prometheus scrape configuration.
391
+
392
+ A provisioned Prometheus and Grafana development stack, including the
393
+ `FlowSense Overview` dashboard, is documented in
394
+ [docs/grafana.md](docs/grafana.md).
395
+ Default operational alerts and Alertmanager routing are documented in
396
+ [docs/alerting.md](docs/alerting.md).
397
+
348
398
  ## Development
349
399
 
350
400
  Run unit tests:
@@ -2,8 +2,11 @@
2
2
 
3
3
  Temporal drift, anomaly detection, and dependency-aware propagation analysis for Apache Airflow.
4
4
 
5
- Current package release: `0.2.1`. See [CHANGELOG.md](CHANGELOG.md) for release
6
- notes and [docs/releasing.md](docs/releasing.md) for the release checklist.
5
+ Current package release: `0.4.0`. See [CHANGELOG.md](CHANGELOG.md) for release
6
+ notes, [docs/architecture.md](docs/architecture.md) for the system architecture,
7
+ [docs/benchmarks.md](docs/benchmarks.md) for performance measurement, and
8
+ [docs/migrating-to-0.3.md](docs/migrating-to-0.3.md) for migration guidance.
9
+ Maintainers can use [docs/releasing.md](docs/releasing.md) as the release checklist.
7
10
 
8
11
  FlowSense analyzes historical DAG executions to identify abnormal task behavior and trace how anomalies propagate through downstream dependencies.
9
12
 
@@ -142,6 +145,7 @@ AIRFLOW_USERNAME=your_username
142
145
  AIRFLOW_PASSWORD=your_password
143
146
  AIRFLOW_API_VERSION=v2
144
147
  AIRFLOW_AUTH_MODE=token
148
+ # AIRFLOW_BEARER_TOKEN=your_static_token
145
149
  AIRFLOW_CONNECT_TIMEOUT=10
146
150
  AIRFLOW_READ_TIMEOUT=10
147
151
  AIRFLOW_MAX_RETRIES=2
@@ -154,6 +158,16 @@ Stable REST API deployments. Airflow 3.x uses the `v2` API and typically uses
154
158
  token authentication. Authentication still depends on the API auth backend
155
159
  configured in the Airflow deployment.
156
160
 
161
+ Static bearer tokens can use `AIRFLOW_AUTH_MODE=bearer` with
162
+ `AIRFLOW_BEARER_TOKEN`; username and password are not required in that mode.
163
+ Library integrations may inject a custom `AirflowAuthProvider` for rotating
164
+ tokens, identity-aware proxies, or deployment-specific headers. See
165
+ [docs/authentication.md](docs/authentication.md) for the complete contract.
166
+
167
+ CI verifies both integrations through versioned Airflow 2 and Airflow 3 REST
168
+ contract fixtures. These boundary tests cover authentication, endpoint routing,
169
+ response validation, domain mapping, and the complete analysis use case.
170
+
157
171
  Transient transport failures and HTTP `429`, `502`, `503`, and `504` responses
158
172
  are retried with exponential backoff. `Retry-After` is honored when Airflow
159
173
  provides it. Connect/read timeouts, retry count, and base backoff can be tuned
@@ -237,22 +251,41 @@ tooling, or client code generation.
237
251
 
238
252
  ## Library API
239
253
 
240
- FlowSense can also be used as a Python library through its supported top-level
241
- API:
254
+ `FlowSenseClient` is the recommended in-process Python API. The environment
255
+ configured Airflow factory keeps the basic setup concise:
242
256
 
243
257
  ```python
244
- from flowsense import AirflowClient, analyze_dag
258
+ from flowsense import FlowSenseClient
259
+ from flowsense.infrastructure.airflow import create_airflow_data_source
245
260
 
246
- with AirflowClient() as source:
247
- analysis = analyze_dag(
248
- dag_id="flowsense_demo",
249
- source=source,
250
- )
261
+ client = FlowSenseClient(create_airflow_data_source)
262
+ analysis = client.analyze("flowsense_demo")
251
263
 
252
264
  print(analysis.overall_severity)
253
265
  print(analysis.primary_origin)
254
266
  ```
255
267
 
268
+ Applications that own configuration can combine `AirflowConfig` with
269
+ `create_airflow_data_source_factory`. Typed integrations may continue to call
270
+ `client.execute(AnalysisRequest(...))`. See the complete
271
+ [Python API guide](docs/python-api.md).
272
+
273
+ Multiple DAGs can be analyzed with bounded, opt-in concurrency. Expected
274
+ FlowSense failures are isolated per DAG:
275
+
276
+ ```python
277
+ result = client.analyze_many(
278
+ ["orders", "payments", "inventory"],
279
+ max_concurrency=3,
280
+ )
281
+ ```
282
+
283
+ `AirflowClient.list_dag_ids()` provides paginated DAG discovery and excludes
284
+ paused DAGs by default.
285
+
286
+ `serialize_batch_analysis(result)` converts the result into the stable batch
287
+ output contract without exposing raw exception objects.
288
+
256
289
  Analysis behavior can be customized with an immutable policy:
257
290
 
258
291
  ```python
@@ -310,6 +343,23 @@ The server exposes the `analyze_airflow_dag` tool, which returns task drift,
310
343
  handoff drift, impact classification, propagation paths, and primary root-cause
311
344
  information for a DAG.
312
345
 
346
+ ## Prometheus
347
+
348
+ Run continuous analysis and expose the latest DAG and bounded task-level metrics:
349
+
350
+ ```bash
351
+ flowsense serve-metrics example_dag --host 0.0.0.0 --port 9108
352
+ ```
353
+
354
+ See [docs/prometheus.md](docs/prometheus.md) for the metric contract,
355
+ cardinality policy, and Prometheus scrape configuration.
356
+
357
+ A provisioned Prometheus and Grafana development stack, including the
358
+ `FlowSense Overview` dashboard, is documented in
359
+ [docs/grafana.md](docs/grafana.md).
360
+ Default operational alerts and Alertmanager routing are documented in
361
+ [docs/alerting.md](docs/alerting.md).
362
+
313
363
  ## Development
314
364
 
315
365
  Run unit tests:
@@ -0,0 +1,57 @@
1
+ # Prometheus alerting
2
+
3
+ The local observability stack loads a tested set of FlowSense alert rules and
4
+ routes firing alerts to Alertmanager.
5
+
6
+ ## Included alerts
7
+
8
+ | Alert | Default condition | Severity |
9
+ | --- | --- | --- |
10
+ | `FlowSenseExporterDown` | Metrics endpoint unavailable for 2 minutes | Critical |
11
+ | `FlowSenseAnalysisFailed` | Latest analysis failed for 2 minutes | Warning |
12
+ | `FlowSenseAnalysisStale` | No successful analysis for 10 minutes in total | Warning |
13
+ | `FlowSenseCriticalDAG` | DAG severity remains critical for 2 minutes | Critical |
14
+ | `FlowSenseCriticalTask` | Task severity remains critical for 2 minutes | Critical |
15
+ | `FlowSenseTaskMetricsDropped` | Cardinality limit omits tasks for 5 minutes | Warning |
16
+ | `FlowSenseAnalysisSlow` | Analysis remains above 30 seconds for 5 minutes | Warning |
17
+
18
+ The stale rule expression becomes true after five minutes without a successful
19
+ analysis and must remain true for another five minutes before firing. Adjust
20
+ thresholds in `deploy/observability/alerts.yml` to match the configured analysis
21
+ interval and operational expectations.
22
+
23
+ ## Validate rules
24
+
25
+ Run syntax and unit checks with the Prometheus image pinned by the Compose file:
26
+
27
+ ```bash
28
+ docker run --rm \
29
+ --entrypoint promtool \
30
+ -v "$PWD/deploy/observability:/workspace:ro" \
31
+ -w /workspace \
32
+ prom/prometheus:v3.14.0 \
33
+ check rules alerts.yml
34
+
35
+ docker run --rm \
36
+ --entrypoint promtool \
37
+ -v "$PWD/deploy/observability:/workspace:ro" \
38
+ -w /workspace \
39
+ prom/prometheus:v3.14.0 \
40
+ test rules alerts.test.yml
41
+ ```
42
+
43
+ ## Configure notifications
44
+
45
+ The committed Alertmanager configuration intentionally uses an empty local
46
+ receiver. Firing alerts are visible in the Alertmanager UI and the Grafana
47
+ dashboard, but no external notification is sent by default.
48
+
49
+ For production, replace the receiver in
50
+ `deploy/observability/alertmanager.yml` with the organization's managed email,
51
+ Slack, PagerDuty, webhook, or other supported integration. Keep credentials out
52
+ of Git and inject them through the deployment platform's secret management
53
+ mechanism.
54
+
55
+ Alertmanager is available locally at `http://localhost:9093`. Verify routing and
56
+ notification delivery in a non-production environment before enabling it for
57
+ operational incidents.
@@ -0,0 +1,138 @@
1
+ # FlowSense Architecture
2
+
3
+ FlowSense uses an inward-facing layered architecture. Delivery mechanisms and
4
+ external systems depend on application contracts; the analysis domain does not
5
+ depend on Airflow, CLI, MCP, or environment configuration.
6
+
7
+ ## Dependency direction
8
+
9
+ ```text
10
+ CLI / MCP
11
+ |
12
+ v
13
+ Application use cases -----> Domain models and policies
14
+ | ^
15
+ v |
16
+ Application ports <----- Infrastructure adapters
17
+ |
18
+ v
19
+ Apache Airflow
20
+ ```
21
+
22
+ Dependencies must point toward the application and domain layers. The
23
+ composition roots in the CLI and MCP adapters are responsible for selecting
24
+ concrete infrastructure implementations.
25
+
26
+ ## Layers
27
+
28
+ ### Domain
29
+
30
+ `flowsense.domain` contains the analysis vocabulary: task runs, policies,
31
+ severity and impact classifications, results, and expected domain failures.
32
+ It must not import application, infrastructure, CLI, MCP, or compatibility
33
+ modules. Domain entities and results use standard-library dataclasses; they do
34
+ not depend on validation or transport frameworks.
35
+
36
+ `flowsense.engine` currently contains pure analysis services such as drift,
37
+ trend, handoff, propagation, and root-cause calculations. These services may
38
+ depend on the domain but not on delivery or infrastructure code.
39
+
40
+ ### Application
41
+
42
+ `flowsense.application` coordinates use cases without knowing how Airflow data
43
+ is retrieved or how results are displayed.
44
+
45
+ `AnalysisRequest` is the validated input contract. `AnalyzeDAG.execute()` is the
46
+ primary use-case boundary. `DAGDataSource` and `DAGDataSourceFactory` are ports
47
+ implemented by infrastructure adapters. The existing functional
48
+ `analyze_dag(dag_id, source, policy)` API remains available for callers that
49
+ already own a data source.
50
+
51
+ `FlowSenseClient` is the supported convenience facade for embedded Python
52
+ consumers. It translates the concise `analyze(...)` call into an
53
+ `AnalysisRequest` and delegates to `AnalyzeDAG`; it contains no collection or
54
+ analysis logic of its own. Its optional multi-DAG operation creates one
55
+ use-case execution and context-managed data source per DAG, preserving the
56
+ single-DAG application boundary.
57
+
58
+ `BatchAnalysisDocument` is a separate versioned envelope around the existing
59
+ single-DAG `AnalysisDocument`. Expected failures cross the output boundary as
60
+ typed failure records rather than runtime exception objects.
61
+
62
+ `DAGAnalysisEngine` is the coarse-grained algorithm extension port. The default
63
+ implementation owns the complete statistical workflow after data collection.
64
+ Alternative engines can be injected into `AnalyzeDAG` without changing CLI,
65
+ MCP, data-source, or output-contract code. Individual detector interfaces are
66
+ intentionally avoided until independent detector replacement is required.
67
+
68
+ ### Infrastructure
69
+
70
+ `flowsense.infrastructure.airflow` implements Airflow HTTP access. Pydantic DTOs
71
+ validate external API payloads and mappers translate them into domain objects.
72
+ Pydantic is intentionally restricted to infrastructure DTOs and application
73
+ output contracts, where runtime boundary validation and JSON Schema generation
74
+ are required.
75
+
76
+ `AirflowClient` receives an explicit `AirflowConfig` and never reads environment
77
+ variables. `load_airflow_config()` and `create_airflow_data_source()` belong to
78
+ the infrastructure composition boundary.
79
+
80
+ Authentication is supplied through `AirflowAuthProvider`. Built-in providers
81
+ cover Basic Auth, Airflow login-token exchange, static bearer tokens, and custom
82
+ headers without coupling request collection to a deployment's identity system.
83
+
84
+ ### Delivery adapters
85
+
86
+ `flowsense.cli`, `flowsense.mcp`, and `flowsense.observability` translate user input into an
87
+ `AnalysisRequest`, invoke `AnalyzeDAG`, and translate the result into their own
88
+ output mechanism. Prometheus export implements the `AnalysisMetricsSink` port
89
+ and keeps a bounded latest-value snapshot. Business analysis and data-collection
90
+ orchestration must not be duplicated in these adapters.
91
+
92
+ ## Main analysis flow
93
+
94
+ ```text
95
+ Input options
96
+ -> AnalysisRequest
97
+ -> AnalyzeDAG
98
+ -> DAGDataSourceFactory
99
+ -> DAGDataSource
100
+ -> task and handoff histories
101
+ -> drift / change-point / trend analysis
102
+ -> impact and propagation analysis
103
+ -> primary root-cause selection
104
+ -> DAGAnalysis
105
+ -> versioned AnalysisDocument
106
+ ```
107
+
108
+ ## Extension points
109
+
110
+ - Add another data source by implementing `DAGDataSource` and providing a
111
+ `DAGDataSourceFactory`.
112
+ - Replace the complete analysis workflow by implementing `DAGAnalysisEngine`
113
+ and injecting it into `AnalyzeDAG`.
114
+ - Add a delivery mechanism by creating an adapter that builds an
115
+ `AnalysisRequest` and invokes `AnalyzeDAG`.
116
+ - Extend output consumers through `AnalysisDocument`; incompatible contract
117
+ changes require a schema-version change.
118
+ - Keep external payload models inside their infrastructure adapter and map them
119
+ into domain models before analysis.
120
+
121
+ ## Compatibility policy
122
+
123
+ The following pre-layered import paths remain temporarily available:
124
+
125
+ - `flowsense.models` -> `flowsense.domain`
126
+ - `flowsense.collector` -> `flowsense.infrastructure.airflow`
127
+ - `flowsense.config` -> `flowsense.infrastructure.airflow`
128
+ - `flowsense.engine.analyzer` -> `flowsense.AnalyzeDAG`
129
+
130
+ They emit `DeprecationWarning` and must not be used by new internal code. They
131
+ will be removed only in an explicitly announced breaking release.
132
+
133
+ ## Automated guardrails
134
+
135
+ `tests/test_architecture.py` parses internal imports and fails when a layer
136
+ introduces a forbidden outward dependency. The compatibility analyzer is the
137
+ only documented exception. New exceptions require an architectural decision and
138
+ must not be added merely to make the test pass.
@@ -0,0 +1,77 @@
1
+ # Airflow authentication
2
+
3
+ FlowSense keeps Airflow authentication behind the `AirflowAuthProvider` port.
4
+ The collector does not need to know whether credentials come from Airflow,
5
+ a reverse proxy, a secret manager, or an external identity system.
6
+
7
+ ## Built-in modes
8
+
9
+ ### Airflow login token
10
+
11
+ This remains the default for Airflow 3 deployments that expose `/auth/token`:
12
+
13
+ ```env
14
+ AIRFLOW_AUTH_MODE=token
15
+ AIRFLOW_USERNAME=airflow
16
+ AIRFLOW_PASSWORD=secret
17
+ ```
18
+
19
+ FlowSense exchanges the credentials once and caches the returned bearer token
20
+ for the lifetime of the client.
21
+
22
+ ### Basic authentication
23
+
24
+ Common Airflow 2 Stable REST API deployments can use:
25
+
26
+ ```env
27
+ AIRFLOW_API_VERSION=v1
28
+ AIRFLOW_AUTH_MODE=basic
29
+ AIRFLOW_USERNAME=airflow
30
+ AIRFLOW_PASSWORD=secret
31
+ ```
32
+
33
+ ### Static bearer token
34
+
35
+ For deployments that issue a token outside Airflow:
36
+
37
+ ```env
38
+ AIRFLOW_AUTH_MODE=bearer
39
+ AIRFLOW_BEARER_TOKEN=secret-token
40
+ ```
41
+
42
+ Username and password are not required in bearer mode. Secrets are excluded
43
+ from the `AirflowConfig` representation, but applications must still keep them
44
+ out of logs, shell history, source control, and command-line arguments.
45
+
46
+ ## Custom provider
47
+
48
+ Enterprise integrations can inject an `AirflowAuthProvider` directly. This is
49
+ appropriate for rotating tokens, identity-aware proxies, workload identity, or
50
+ deployment-specific headers:
51
+
52
+ ```python
53
+ from flowsense.infrastructure.airflow import (
54
+ AirflowClient,
55
+ AirflowConfig,
56
+ HeaderAuthProvider,
57
+ )
58
+
59
+ provider = HeaderAuthProvider({"X-Forwarded-User": "flowsense"})
60
+ client = AirflowClient(
61
+ AirflowConfig(base_url="https://airflow.example.com"),
62
+ auth_provider=provider,
63
+ )
64
+ ```
65
+
66
+ For rotating bearer credentials, pass a callable. It is evaluated for every
67
+ Airflow request:
68
+
69
+ ```python
70
+ from flowsense.infrastructure.airflow import BearerTokenAuthProvider
71
+
72
+ provider = BearerTokenAuthProvider(load_current_token)
73
+ ```
74
+
75
+ Custom providers should return only authentication material and leave request
76
+ execution, retry, timeout, payload validation, and error translation to
77
+ `AirflowClient`.
@@ -0,0 +1,37 @@
1
+ # Performance benchmarks
2
+
3
+ FlowSense includes a deterministic synthetic benchmark for measuring the
4
+ default analysis engine independently from Airflow and network latency.
5
+
6
+ The benchmark creates an ordered chain DAG, generates repeatable task timings,
7
+ injects anomalies into the latest run, and reports:
8
+
9
+ - analysis execution time;
10
+ - total time including versioned output serialization;
11
+ - peak Python memory observed through `tracemalloc`;
12
+ - scenario dimensions and analyzed task count.
13
+
14
+ Run the default scenario:
15
+
16
+ ```bash
17
+ uv run python -m benchmarks.benchmark_analysis
18
+ ```
19
+
20
+ Run a larger scenario and save machine-readable results:
21
+
22
+ ```bash
23
+ uv run python -m benchmarks.benchmark_analysis \
24
+ --tasks 500 \
25
+ --runs 100 \
26
+ --iterations 5 \
27
+ --output benchmark-results.json
28
+ ```
29
+
30
+ The GitHub `Analysis benchmark` workflow provides the same parameters and
31
+ uploads the JSON result as a workflow artifact.
32
+
33
+ Benchmark results are observational and do not use hard pass/fail time limits.
34
+ Shared CI runners have variable performance, so fixed thresholds would create
35
+ flaky checks. Compare runs produced with the same scenario, Python version, and
36
+ similar hardware. Introduce performance gates only after a stable baseline has
37
+ been collected on controlled runners.