flowsense-engine 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 (83) hide show
  1. flowsense_engine-0.2.1/CHANGELOG.md +48 -0
  2. flowsense_engine-0.2.1/LICENSE +17 -0
  3. flowsense_engine-0.2.1/MANIFEST.in +2 -0
  4. flowsense_engine-0.2.1/PKG-INFO +447 -0
  5. flowsense_engine-0.2.1/README.md +412 -0
  6. flowsense_engine-0.2.1/docs/releasing.md +43 -0
  7. flowsense_engine-0.2.1/pyproject.toml +79 -0
  8. flowsense_engine-0.2.1/setup.cfg +4 -0
  9. flowsense_engine-0.2.1/src/flowsense/__init__.py +77 -0
  10. flowsense_engine-0.2.1/src/flowsense/application/__init__.py +20 -0
  11. flowsense_engine-0.2.1/src/flowsense/application/analyzer.py +68 -0
  12. flowsense_engine-0.2.1/src/flowsense/application/output.py +128 -0
  13. flowsense_engine-0.2.1/src/flowsense/application/pipeline.py +210 -0
  14. flowsense_engine-0.2.1/src/flowsense/application/ports.py +11 -0
  15. flowsense_engine-0.2.1/src/flowsense/application/request.py +32 -0
  16. flowsense_engine-0.2.1/src/flowsense/application/serialization.py +177 -0
  17. flowsense_engine-0.2.1/src/flowsense/cli/__init__.py +0 -0
  18. flowsense_engine-0.2.1/src/flowsense/cli/main.py +195 -0
  19. flowsense_engine-0.2.1/src/flowsense/cli/report.py +233 -0
  20. flowsense_engine-0.2.1/src/flowsense/collector/__init__.py +0 -0
  21. flowsense_engine-0.2.1/src/flowsense/collector/airflow_client.py +5 -0
  22. flowsense_engine-0.2.1/src/flowsense/config.py +72 -0
  23. flowsense_engine-0.2.1/src/flowsense/domain/__init__.py +52 -0
  24. flowsense_engine-0.2.1/src/flowsense/domain/enums.py +47 -0
  25. flowsense_engine-0.2.1/src/flowsense/domain/exceptions.py +25 -0
  26. flowsense_engine-0.2.1/src/flowsense/domain/models.py +19 -0
  27. flowsense_engine-0.2.1/src/flowsense/domain/policy.py +55 -0
  28. flowsense_engine-0.2.1/src/flowsense/domain/results.py +187 -0
  29. flowsense_engine-0.2.1/src/flowsense/engine/__init__.py +0 -0
  30. flowsense_engine-0.2.1/src/flowsense/engine/analyzer.py +17 -0
  31. flowsense_engine-0.2.1/src/flowsense/engine/change_point.py +87 -0
  32. flowsense_engine-0.2.1/src/flowsense/engine/drift.py +80 -0
  33. flowsense_engine-0.2.1/src/flowsense/engine/history.py +34 -0
  34. flowsense_engine-0.2.1/src/flowsense/engine/impact.py +51 -0
  35. flowsense_engine-0.2.1/src/flowsense/engine/propagation.py +173 -0
  36. flowsense_engine-0.2.1/src/flowsense/engine/root_cause.py +148 -0
  37. flowsense_engine-0.2.1/src/flowsense/engine/timing.py +221 -0
  38. flowsense_engine-0.2.1/src/flowsense/engine/trend.py +82 -0
  39. flowsense_engine-0.2.1/src/flowsense/infrastructure/__init__.py +1 -0
  40. flowsense_engine-0.2.1/src/flowsense/infrastructure/airflow/__init__.py +13 -0
  41. flowsense_engine-0.2.1/src/flowsense/infrastructure/airflow/client.py +373 -0
  42. flowsense_engine-0.2.1/src/flowsense/infrastructure/airflow/dto.py +33 -0
  43. flowsense_engine-0.2.1/src/flowsense/infrastructure/airflow/exceptions.py +31 -0
  44. flowsense_engine-0.2.1/src/flowsense/infrastructure/airflow/mapper.py +27 -0
  45. flowsense_engine-0.2.1/src/flowsense/mcp/__init__.py +0 -0
  46. flowsense_engine-0.2.1/src/flowsense/mcp/server.py +75 -0
  47. flowsense_engine-0.2.1/src/flowsense/models/__init__.py +7 -0
  48. flowsense_engine-0.2.1/src/flowsense/models/dag_analysis.py +3 -0
  49. flowsense_engine-0.2.1/src/flowsense/models/task_run.py +3 -0
  50. flowsense_engine-0.2.1/src/flowsense/py.typed +0 -0
  51. flowsense_engine-0.2.1/src/flowsense/version.py +6 -0
  52. flowsense_engine-0.2.1/src/flowsense_engine.egg-info/PKG-INFO +447 -0
  53. flowsense_engine-0.2.1/src/flowsense_engine.egg-info/SOURCES.txt +81 -0
  54. flowsense_engine-0.2.1/src/flowsense_engine.egg-info/dependency_links.txt +1 -0
  55. flowsense_engine-0.2.1/src/flowsense_engine.egg-info/entry_points.txt +3 -0
  56. flowsense_engine-0.2.1/src/flowsense_engine.egg-info/requires.txt +14 -0
  57. flowsense_engine-0.2.1/src/flowsense_engine.egg-info/top_level.txt +1 -0
  58. flowsense_engine-0.2.1/tests/test_airflow_client.py +313 -0
  59. flowsense_engine-0.2.1/tests/test_airflow_client_integration.py +73 -0
  60. flowsense_engine-0.2.1/tests/test_airflow_compatibility.py +237 -0
  61. flowsense_engine-0.2.1/tests/test_airflow_mapper.py +58 -0
  62. flowsense_engine-0.2.1/tests/test_airflow_resilience.py +150 -0
  63. flowsense_engine-0.2.1/tests/test_analysis_output.py +76 -0
  64. flowsense_engine-0.2.1/tests/test_analysis_pipeline.py +46 -0
  65. flowsense_engine-0.2.1/tests/test_analysis_request.py +36 -0
  66. flowsense_engine-0.2.1/tests/test_analyzer.py +473 -0
  67. flowsense_engine-0.2.1/tests/test_change_point.py +53 -0
  68. flowsense_engine-0.2.1/tests/test_cli.py +248 -0
  69. flowsense_engine-0.2.1/tests/test_cli_report.py +140 -0
  70. flowsense_engine-0.2.1/tests/test_dag_summary.py +134 -0
  71. flowsense_engine-0.2.1/tests/test_domain.py +46 -0
  72. flowsense_engine-0.2.1/tests/test_drift.py +85 -0
  73. flowsense_engine-0.2.1/tests/test_history.py +128 -0
  74. flowsense_engine-0.2.1/tests/test_impact.py +149 -0
  75. flowsense_engine-0.2.1/tests/test_mcp_integration.py +54 -0
  76. flowsense_engine-0.2.1/tests/test_mcp_server.py +319 -0
  77. flowsense_engine-0.2.1/tests/test_package_metadata.py +11 -0
  78. flowsense_engine-0.2.1/tests/test_policy.py +97 -0
  79. flowsense_engine-0.2.1/tests/test_propagation.py +426 -0
  80. flowsense_engine-0.2.1/tests/test_public_api.py +51 -0
  81. flowsense_engine-0.2.1/tests/test_root_cause.py +281 -0
  82. flowsense_engine-0.2.1/tests/test_timing.py +260 -0
  83. flowsense_engine-0.2.1/tests/test_trend.py +54 -0
@@ -0,0 +1,48 @@
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
+ ## [0.2.1] - 2026-09-10
7
+
8
+ ### Changed
9
+
10
+ - Renamed the Python distribution to `flowsense-engine` while preserving the
11
+ `flowsense` import package and CLI command.
12
+ - Added tokenless PyPI publishing through GitHub Actions Trusted Publishing.
13
+
14
+ ## [0.2.0] - 2026-09-10
15
+
16
+ ### Added
17
+
18
+ - Task handoff drift, impact classification, propagation, and root-cause analysis.
19
+ - Configurable drift, mapped-task aggregation, change-point, and trend policies.
20
+ - Apache Airflow 2 and 3 API compatibility with pagination and bounded history.
21
+ - Retry, timeout, backoff, and `Retry-After` handling for Airflow requests.
22
+ - Historical DAG run analysis with preceding-history isolation.
23
+ - Rich CLI reports, JSON output, severity-based exit thresholds, and schema output.
24
+ - MCP analysis tool with the same policy and versioned response contract as the CLI.
25
+ - Typed `AnalysisRequest` and `AnalysisDocument` contracts with JSON Schema support.
26
+ - Supported top-level library API, typed package marker, and Python 3.12/3.13 CI.
27
+
28
+ ### Changed
29
+
30
+ - Split the analysis workflow into domain, application, infrastructure, and adapter
31
+ boundaries.
32
+ - Analysis output schema advanced to version `1.1` with `current_dag_run_id`.
33
+ - Expected configuration, Airflow API, and payload failures now use a unified error
34
+ hierarchy.
35
+
36
+ ### Fixed
37
+
38
+ - Preserved anomaly origins across normal dependency gaps.
39
+ - Detected drift when the historical median absolute deviation is zero.
40
+ - Aggregated dynamically mapped task instances before analysis.
41
+ - Reported insufficient history and invalid handoff timing as diagnostics.
42
+
43
+ ## [0.1.0] - 2026-08-18
44
+
45
+ ### Added
46
+
47
+ - Initial FlowSense prototype with Airflow task-duration collection and robust
48
+ median/MAD drift detection.
@@ -0,0 +1,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2026 Omer Cengiz
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
@@ -0,0 +1,2 @@
1
+ include CHANGELOG.md
2
+ include docs/releasing.md
@@ -0,0 +1,447 @@
1
+ Metadata-Version: 2.4
2
+ Name: flowsense-engine
3
+ Version: 0.2.1
4
+ Summary: Temporal drift and anomaly detection for Apache Airflow
5
+ Author: Omer Cengiz
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/omercengiz/flowsense-engine
8
+ Project-URL: Issues, https://github.com/omercengiz/flowsense-engine/issues
9
+ Project-URL: Repository, https://github.com/omercengiz/flowsense-engine
10
+ Project-URL: Changelog, https://github.com/omercengiz/flowsense-engine/blob/master/CHANGELOG.md
11
+ Keywords: airflow,anomaly-detection,data-engineering,observability
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.12
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: httpx>=0.27
23
+ Requires-Dist: pydantic>=2.8
24
+ Requires-Dist: numpy>=2.0
25
+ Requires-Dist: typer>=0.12
26
+ Requires-Dist: rich>=13.7
27
+ Provides-Extra: dev
28
+ Requires-Dist: build>=1.2; extra == "dev"
29
+ Requires-Dist: pyright>=1.1.400; extra == "dev"
30
+ Requires-Dist: pytest>=8.0; extra == "dev"
31
+ Requires-Dist: ruff>=0.6; extra == "dev"
32
+ Provides-Extra: mcp
33
+ Requires-Dist: mcp[cli]>=2.0; extra == "mcp"
34
+ Dynamic: license-file
35
+
36
+ # FlowSense Engine
37
+
38
+ Temporal drift, anomaly detection, and dependency-aware propagation analysis for Apache Airflow.
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.
42
+
43
+ FlowSense analyzes historical DAG executions to identify abnormal task behavior and trace how anomalies propagate through downstream dependencies.
44
+
45
+ ## Why FlowSense?
46
+
47
+ Airflow provides rich execution metadata, but identifying behavioral drift across historical runs still requires manual analysis.
48
+
49
+ FlowSense is designed to answer questions such as:
50
+
51
+ - Which task started behaving differently?
52
+ - How large is the deviation from its historical baseline?
53
+ - Is the anomaly isolated or affecting downstream tasks?
54
+ - Where is the most likely origin of the slowdown?
55
+
56
+ ## Current Features
57
+
58
+ - Apache Airflow 3 REST API integration
59
+ - JWT-based Airflow authentication
60
+ - DAG run collection
61
+ - Task instance collection
62
+ - Automatic DAG dependency discovery
63
+ - Task duration history generation
64
+ - Median-based historical baselines
65
+ - MAD-based robust Z-score drift detection
66
+ - Severity classification
67
+ - Task handoff delay analysis
68
+ - Task impact classification (`OWN_DRIFT`, `INHERITED_DELAY`, and `COMBINED`)
69
+ - Multi-hop and branching propagation analysis
70
+ - Primary root-cause selection
71
+ - CLI-based DAG analysis
72
+ - MCP server integration
73
+
74
+ ## Example
75
+
76
+ ```bash
77
+ flowsense analyze flowsense_demo
78
+ ```
79
+
80
+ Example output:
81
+
82
+ ```text
83
+ FlowSense Analysis — flowsense_demo
84
+
85
+ ┏━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┓
86
+ ┃ Task ┃ Baseline ┃ Current ┃ Deviation ┃ Z-Score ┃ Severity ┃ Impact ┃
87
+ ┡━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━┩
88
+ │ extract │ 1.56s │ 1.61s │ +3.4% │ 0.17 │ NORMAL │ NORMAL │
89
+ │ transform │ 3.34s │ 9.61s │ +187.6% │ 7.61 │ CRITICAL │ OWN_DRIFT │
90
+ │ load │ 1.40s │ 2.11s │ +50.2% │ 3.17 │ MEDIUM │ COMBINED │
91
+ └───────────┴──────────┴─────────┴───────────┴─────────┴──────────┴───────────┘
92
+
93
+ Overall Severity: CRITICAL
94
+ Primary Origin: transform
95
+ Reason: OWN_DRIFT
96
+ Severity: CRITICAL
97
+ Propagation Score: 0.33
98
+
99
+ Propagation Analysis
100
+
101
+ Origin: transform
102
+ Path: transform -> load
103
+ Propagation Score: 0.33
104
+ ```
105
+
106
+ ## Architecture
107
+
108
+ ```text
109
+ Apache Airflow
110
+
111
+
112
+ Collector
113
+
114
+
115
+ Task Run and Handoff History
116
+
117
+
118
+ Drift and Impact Analysis
119
+
120
+
121
+ Propagation and Root-Cause Analysis
122
+
123
+ ├── CLI
124
+ └── MCP Server
125
+ ```
126
+
127
+ ## Installation
128
+
129
+ FlowSense currently requires Python 3.12 or newer.
130
+ Python 3.12 and 3.13 are covered by the CI test matrix.
131
+
132
+ Clone the repository:
133
+
134
+ ```bash
135
+ git clone <repository-url>
136
+ cd flowsense-engine
137
+ ```
138
+
139
+ Create a virtual environment and install the project:
140
+
141
+ ```bash
142
+ uv venv --python 3.12
143
+ source .venv/bin/activate
144
+ uv pip install -e ".[dev,mcp]"
145
+ ```
146
+
147
+ Once published on PyPI, install the distribution with:
148
+
149
+ ```bash
150
+ pip install flowsense-engine
151
+ ```
152
+
153
+ The distribution name is `flowsense-engine`; Python imports and CLI commands
154
+ remain `flowsense`.
155
+
156
+ Check the installed package version:
157
+
158
+ ```bash
159
+ flowsense --version
160
+ ```
161
+
162
+ ## Configuration
163
+
164
+ FlowSense connects to Apache Airflow through its REST API.
165
+
166
+ Copy the example environment file:
167
+
168
+ ```bash
169
+ cp .env.example .env
170
+ ```
171
+
172
+ Configure:
173
+
174
+ ```env
175
+ AIRFLOW_BASE_URL=http://localhost:8080
176
+ AIRFLOW_USERNAME=your_username
177
+ AIRFLOW_PASSWORD=your_password
178
+ AIRFLOW_API_VERSION=v2
179
+ AIRFLOW_AUTH_MODE=token
180
+ AIRFLOW_CONNECT_TIMEOUT=10
181
+ AIRFLOW_READ_TIMEOUT=10
182
+ AIRFLOW_MAX_RETRIES=2
183
+ AIRFLOW_RETRY_BACKOFF=0.5
184
+ AIRFLOW_HISTORY_RUN_LIMIT=100
185
+ ```
186
+
187
+ Use `AIRFLOW_API_VERSION=v1` with `AIRFLOW_AUTH_MODE=basic` for Airflow 2.x
188
+ Stable REST API deployments. Airflow 3.x uses the `v2` API and typically uses
189
+ token authentication. Authentication still depends on the API auth backend
190
+ configured in the Airflow deployment.
191
+
192
+ Transient transport failures and HTTP `429`, `502`, `503`, and `504` responses
193
+ are retried with exponential backoff. `Retry-After` is honored when Airflow
194
+ provides it. Connect/read timeouts, retry count, and base backoff can be tuned
195
+ with the environment variables above; permanent client errors are returned
196
+ without retrying.
197
+
198
+ Load the environment variables:
199
+
200
+ ```bash
201
+ export $(grep -v '^#' .env | xargs)
202
+ ```
203
+
204
+ Then run:
205
+
206
+ ```bash
207
+ flowsense analyze <dag_id>
208
+ ```
209
+
210
+ The CLI report includes a DAG summary and separate tables for task drift,
211
+ handoff drift, change points, trends, propagation paths, and diagnostics.
212
+ Results are ordered by severity or subject so repeated analyses remain easy to
213
+ compare.
214
+
215
+ For automation and CI/CD integrations, request the versioned JSON document:
216
+
217
+ ```bash
218
+ flowsense analyze <dag_id> --output json
219
+ ```
220
+
221
+ CI jobs can also fail when the analysis reaches a selected severity:
222
+
223
+ ```bash
224
+ flowsense analyze <dag_id> --output json --fail-on high
225
+ ```
226
+
227
+ `--fail-on` accepts `medium`, `high`, or `critical`. The report is always
228
+ written before FlowSense exits: code `0` means the severity is below the
229
+ threshold, code `2` means the threshold was reached, and code `1` remains
230
+ reserved for analysis or Airflow request failures.
231
+
232
+ By default, FlowSense collects task instances for the most recent 100
233
+ successful DAG runs. This prevents long-lived DAGs from generating an
234
+ unbounded number of task-instance API requests. Change the default with
235
+ `AIRFLOW_HISTORY_RUN_LIMIT`, or override it for one CLI analysis:
236
+
237
+ ```bash
238
+ flowsense analyze <dag_id> --history-run-limit 250
239
+ ```
240
+
241
+ The MCP tool exposes the same override as `history_run_limit`. The limit must
242
+ be at least `2` and cannot be lower than `minimum_history`; contradictory
243
+ requests are rejected before FlowSense connects to Airflow.
244
+
245
+ To investigate or backtest a specific successful DAG run, select it as the
246
+ current observation. FlowSense excludes every newer run from its baseline:
247
+
248
+ ```bash
249
+ flowsense analyze <dag_id> --dag-run-id <dag_run_id>
250
+ ```
251
+
252
+ The MCP tool exposes the same option as `dag_run_id`. Analysis output includes
253
+ `current_dag_run_id`; this field was introduced in output schema version `1.1`.
254
+
255
+ The JSON document and MCP tool response share the same serialization contract
256
+ and include a `schema_version` field. The serializer is also available from the
257
+ public library API as `flowsense.serialize_analysis`.
258
+
259
+ For typed integrations, `flowsense.build_analysis_document` returns a
260
+ validated Pydantic `AnalysisDocument`. Its versioned JSON Schema is available
261
+ through `flowsense.analysis_json_schema()`, allowing consumers to validate or
262
+ generate models for the CLI and MCP response contract.
263
+
264
+ The same schema can be emitted without connecting to Airflow:
265
+
266
+ ```bash
267
+ flowsense schema > flowsense-analysis.schema.json
268
+ ```
269
+
270
+ This output is deterministic and can be used in CI contract checks, editor
271
+ tooling, or client code generation.
272
+
273
+ ## Library API
274
+
275
+ FlowSense can also be used as a Python library through its supported top-level
276
+ API:
277
+
278
+ ```python
279
+ from flowsense import AirflowClient, analyze_dag
280
+
281
+ with AirflowClient() as source:
282
+ analysis = analyze_dag(
283
+ dag_id="flowsense_demo",
284
+ source=source,
285
+ )
286
+
287
+ print(analysis.overall_severity)
288
+ print(analysis.primary_origin)
289
+ ```
290
+
291
+ Analysis behavior can be customized with an immutable policy:
292
+
293
+ ```python
294
+ from flowsense import AnalysisPolicy, MappedTaskAggregation
295
+
296
+ policy = AnalysisPolicy(
297
+ minimum_history=10,
298
+ baseline_window=30,
299
+ medium_threshold=2.5,
300
+ high_threshold=4.0,
301
+ critical_threshold=6.0,
302
+ mapped_task_aggregation=MappedTaskAggregation.MAX,
303
+ change_point_minimum_segment_size=4,
304
+ change_point_score_threshold=4.0,
305
+ trend_minimum_observations=8,
306
+ trend_score_threshold=4.0,
307
+ trend_minimum_directional_consistency=0.75,
308
+ )
309
+ ```
310
+
311
+ `baseline_window` limits the number of historical values used before the current
312
+ run. Mapped task durations can be aggregated with `MAX`, `MEAN`, or `SUM`. The
313
+ same policy options are available through the CLI and MCP tool.
314
+ Change-point and trend detection can also be disabled independently with
315
+ `change_point_detection_enabled=False` or `trend_detection_enabled=False`.
316
+
317
+ CLI and MCP inputs are normalized into an immutable `AnalysisRequest` before
318
+ execution. Library integrations may use the same public DTO when they need to
319
+ validate a DAG id, policy, and explicit history collection limit together.
320
+
321
+ Every `DAGAnalysis` exposes a derived `summary` with task-analysis coverage,
322
+ severity distribution, anomalous task and handoff counts, uniquely affected
323
+ tasks, structural signals, and diagnostics. The same DAG-level summary is
324
+ included in CLI and MCP output.
325
+
326
+ Custom data sources can implement the `DAGDataSource` protocol and be passed to
327
+ `analyze_dag`. Names exported directly from `flowsense` form the supported public
328
+ API. Imports from internal packages such as `flowsense.engine` should be treated
329
+ as implementation details and may change before version 1.0.
330
+
331
+ Expected operational failures derive from `FlowSenseError`. Library consumers
332
+ can catch `ConfigurationError`, `AirflowApiError`, or `AirflowDataError` for
333
+ more specific handling. CLI failures return exit code `1`; MCP converts these
334
+ failures into tool errors without exposing response bodies or parser details.
335
+
336
+ ## MCP Server
337
+
338
+ Start the FlowSense MCP server over stdio:
339
+
340
+ ```bash
341
+ flowsense-mcp
342
+ ```
343
+
344
+ The server exposes the `analyze_airflow_dag` tool, which returns task drift,
345
+ handoff drift, impact classification, propagation paths, and primary root-cause
346
+ information for a DAG.
347
+
348
+ ## Development
349
+
350
+ Run unit tests:
351
+
352
+ ```bash
353
+ python -m pytest -m "not integration" -v
354
+ ```
355
+
356
+ Run the complete test suite when a local Airflow instance is available:
357
+
358
+ ```bash
359
+ python -m pytest -v
360
+ ```
361
+
362
+ Lint:
363
+
364
+ ```bash
365
+ ruff check .
366
+ ```
367
+
368
+ Check formatting:
369
+
370
+ ```bash
371
+ ruff format --check .
372
+ ```
373
+
374
+ Apply formatting:
375
+
376
+ ```bash
377
+ ruff format .
378
+ ```
379
+
380
+ ## Project Structure
381
+
382
+ ```text
383
+ src/flowsense/
384
+ ├── application/
385
+ │ ├── analyzer.py
386
+ │ ├── pipeline.py
387
+ │ └── ports.py
388
+ ├── domain/
389
+ ├── engine/
390
+ │ ├── change_point.py
391
+ │ ├── trend.py
392
+ │ ├── drift.py
393
+ │ ├── history.py
394
+ │ ├── impact.py
395
+ │ ├── propagation.py
396
+ │ ├── root_cause.py
397
+ │ └── timing.py
398
+ ├── infrastructure/
399
+ │ └── airflow/
400
+ ├── cli/
401
+ ├── mcp/
402
+ ├── collector/ # backward-compatible imports
403
+ └── models/ # backward-compatible imports
404
+ ```
405
+
406
+ ## Detection Approach
407
+
408
+ The current drift detector uses robust statistics rather than machine learning.
409
+
410
+ For each task, historical execution durations are used to calculate a median baseline and Median Absolute Deviation (MAD).
411
+
412
+ The latest execution is compared against that baseline using a robust Z-score.
413
+
414
+ This makes the detector less sensitive to historical outliers than approaches based only on mean and standard deviation.
415
+
416
+ Propagation scores are normalized to the `0.0–1.0` range. Downstream task
417
+ severity is weighted by graph distance with a `0.8` decay per hop, so anomalies
418
+ closer to the origin contribute more strongly than anomalies farther along the
419
+ same path.
420
+
421
+ FlowSense also scans ordered task-duration and handoff-delay histories for
422
+ persistent level shifts. Each candidate split must leave at least three
423
+ observations on both sides. Candidates are compared with a robust, MAD-based
424
+ score, and detected changes report their location, direction, before/after
425
+ medians, percentage change, and score. This prevents a single latest-run outlier
426
+ from being reported as a structural change.
427
+
428
+ FlowSense detects sustained increasing and decreasing trends in ordered task
429
+ durations and handoff delays with a robust Theil-Sen slope. A trend must contain
430
+ at least five observations, meet a minimum directional-consistency ratio, and
431
+ exceed a MAD-based score threshold. Results include the per-run slope, estimated
432
+ total and percentage change, direction, consistency, and score.
433
+
434
+ The application layer coordinates analysis through explicit task and handoff
435
+ pipeline stages. Each stage returns typed drift, structural-signal, and
436
+ diagnostic results, while `analyze_dag` remains the composition point for impact,
437
+ propagation, root-cause, and DAG-level output.
438
+
439
+ ## Project Status
440
+
441
+ FlowSense is currently in early development.
442
+
443
+ The current implementation should be considered experimental and is not yet intended for production use.
444
+
445
+ ## License
446
+
447
+ Licensed under the Apache License 2.0.