hexastack-qual 0.7.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.
- hexastack_qual-0.7.0/PKG-INFO +103 -0
- hexastack_qual-0.7.0/README.md +82 -0
- hexastack_qual-0.7.0/pyproject.toml +75 -0
- hexastack_qual-0.7.0/pyproject.toml.orig +68 -0
- hexastack_qual-0.7.0/src/hexastack_qual/__init__.py +18 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/__init__.py +14 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/cqrs/__init__.py +50 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/cqrs/commands.py +88 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/cqrs/handlers.py +187 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/cqrs/queries.py +90 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/events/__init__.py +13 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/events/publisher.py +105 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/hexaqual/__init__.py +14 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/hexaqual/runner.py +628 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/mcp/__init__.py +37 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/mcp/server.py +77 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/mcp/tools.py +351 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/ui/__init__.py +13 -0
- hexastack_qual-0.7.0/src/hexastack_qual/adapters/ui/panel.py +88 -0
- hexastack_qual-0.7.0/src/hexastack_qual/domain/__init__.py +54 -0
- hexastack_qual-0.7.0/src/hexastack_qual/domain/events.py +93 -0
- hexastack_qual-0.7.0/src/hexastack_qual/domain/exceptions.py +134 -0
- hexastack_qual-0.7.0/src/hexastack_qual/domain/models.py +296 -0
- hexastack_qual-0.7.0/src/hexastack_qual/infra/__init__.py +15 -0
- hexastack_qual-0.7.0/src/hexastack_qual/infra/bootstrap.py +117 -0
- hexastack_qual-0.7.0/src/hexastack_qual/infra/cli.py +75 -0
- hexastack_qual-0.7.0/src/hexastack_qual/ports/__init__.py +20 -0
- hexastack_qual-0.7.0/src/hexastack_qual/ports/auditor.py +122 -0
- hexastack_qual-0.7.0/src/hexastack_qual/ports/diagnostics.py +55 -0
- hexastack_qual-0.7.0/src/hexastack_qual/ports/mutator.py +66 -0
- hexastack_qual-0.7.0/src/hexastack_qual/ports/openssf.py +109 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hexastack-qual
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: Hexagonal quality, governance, and release adapter integrating hexaqual with CQRS, MCP, UI, and Events
|
|
5
|
+
Author: Richard West
|
|
6
|
+
Author-email: Richard West <dopplereffect.us@gmail.com>
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
Requires-Dist: hexaqual[all]>=0.9.0
|
|
9
|
+
Requires-Dist: hexastack-core
|
|
10
|
+
Requires-Dist: hexastack-cqrs
|
|
11
|
+
Requires-Dist: hexastack-qual[events,mcp,ui] ; extra == 'all'
|
|
12
|
+
Requires-Dist: hexastack-events ; extra == 'events'
|
|
13
|
+
Requires-Dist: hexastack-mcp ; extra == 'mcp'
|
|
14
|
+
Requires-Dist: hexastack-ui ; extra == 'ui'
|
|
15
|
+
Requires-Python: >=3.13
|
|
16
|
+
Provides-Extra: all
|
|
17
|
+
Provides-Extra: events
|
|
18
|
+
Provides-Extra: mcp
|
|
19
|
+
Provides-Extra: ui
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# hexastack-qual
|
|
23
|
+
|
|
24
|
+
> 🛡️ **Hexagonal quality, governance, and release adapter integrating hexaqual with CQRS, MCP, UI, and Events.**
|
|
25
|
+
|
|
26
|
+
Part of the [Hexastack](https://dopplereffect.us/hexastack/) ecosystem.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 🌟 Overview
|
|
31
|
+
|
|
32
|
+
`hexastack-qual` bridges [**Hexaqual**](https://github.com/TheTrueSCU/hexaqual) into Hexastack applications:
|
|
33
|
+
- **CQRS Execution Pipeline**: Execute sanity checks, statement formatting, and mutant audits via decoupled `Command` and `Query` objects with full OpenTelemetry tracing and structured logging.
|
|
34
|
+
- **Model Context Protocol (MCP)**: Expose `@mcp_tool` and `@mcp_resource` endpoints for AI coding assistants (Claude Desktop, Cursor, Antigravity) to audit complexity, test parity, and mutant resilience.
|
|
35
|
+
- **DevTools Console Panel**: Mount a reactive NiceGUI dashboard in `/devtools` showing live quality gauges and one-click remediation actions.
|
|
36
|
+
- **Distributed Event Publishing**: Emit domain events (`QualityGateFailedEvent`, `CriticalMutantSurvivingEvent`) over `hexastack-events` to Apprise or NATS.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 📦 Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Core adapter (CQRS pipeline + Hexaqual runner)
|
|
44
|
+
pip install hexastack-qual
|
|
45
|
+
|
|
46
|
+
# With Model Context Protocol (MCP) server support
|
|
47
|
+
pip install "hexastack-qual[mcp]"
|
|
48
|
+
|
|
49
|
+
# With NiceGUI DevTools console tab
|
|
50
|
+
pip install "hexastack-qual[ui]"
|
|
51
|
+
|
|
52
|
+
# With distributed event publishing
|
|
53
|
+
pip install "hexastack-qual[events]"
|
|
54
|
+
|
|
55
|
+
# All extras
|
|
56
|
+
pip install "hexastack-qual[all]"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 🚀 Quickstart
|
|
62
|
+
|
|
63
|
+
### 1. CQRS Pipeline Execution
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from hexastack_cqrs.infra.pipeline import ExecutionPipeline
|
|
67
|
+
from hexastack_qual.adapters.cqrs.commands import RunSanityCheckCommand
|
|
68
|
+
from hexastack_qual.adapters.cqrs.queries import GetQualityScorecardQuery
|
|
69
|
+
|
|
70
|
+
# Dispatch queries and commands via the CQRS pipeline
|
|
71
|
+
scorecard = await pipeline.execute(GetQualityScorecardQuery(package="core"))
|
|
72
|
+
print(
|
|
73
|
+
f"Healthy: {scorecard.is_healthy}, Violations: {len(scorecard.complexity_violations)}"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
result = await pipeline.execute(RunSanityCheckCommand(package="core", skip_tests=True))
|
|
77
|
+
print(f"Sanity status: {result.success}")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 2. Exposing Quality over MCP
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from hexastack_qual.adapters.mcp import get_quality_tools
|
|
84
|
+
|
|
85
|
+
# Attach quality inspection tools to an AI agent
|
|
86
|
+
agent = create_cqrs_agent(
|
|
87
|
+
pipeline=pipeline,
|
|
88
|
+
tools=get_quality_tools(),
|
|
89
|
+
system_prompt="You are an autonomous refactoring agent with self-verification tools.",
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 🏛️ Architecture
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
hexastack_qual/
|
|
99
|
+
├── domain/ # Pure models (QualityScorecard, ComplexityMetric), exceptions, and events
|
|
100
|
+
├── ports/ # QualityAuditorPort, MutationInspectorPort, PrDiagnosticPort
|
|
101
|
+
├── adapters/ # Hexaqual runner, CQRS handlers, MCP tools, NiceGUI panel, Events publisher
|
|
102
|
+
└── infra/ # QualBootstrapper (DI container registration) and Typer CLI
|
|
103
|
+
```
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# hexastack-qual
|
|
2
|
+
|
|
3
|
+
> 🛡️ **Hexagonal quality, governance, and release adapter integrating hexaqual with CQRS, MCP, UI, and Events.**
|
|
4
|
+
|
|
5
|
+
Part of the [Hexastack](https://dopplereffect.us/hexastack/) ecosystem.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🌟 Overview
|
|
10
|
+
|
|
11
|
+
`hexastack-qual` bridges [**Hexaqual**](https://github.com/TheTrueSCU/hexaqual) into Hexastack applications:
|
|
12
|
+
- **CQRS Execution Pipeline**: Execute sanity checks, statement formatting, and mutant audits via decoupled `Command` and `Query` objects with full OpenTelemetry tracing and structured logging.
|
|
13
|
+
- **Model Context Protocol (MCP)**: Expose `@mcp_tool` and `@mcp_resource` endpoints for AI coding assistants (Claude Desktop, Cursor, Antigravity) to audit complexity, test parity, and mutant resilience.
|
|
14
|
+
- **DevTools Console Panel**: Mount a reactive NiceGUI dashboard in `/devtools` showing live quality gauges and one-click remediation actions.
|
|
15
|
+
- **Distributed Event Publishing**: Emit domain events (`QualityGateFailedEvent`, `CriticalMutantSurvivingEvent`) over `hexastack-events` to Apprise or NATS.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 📦 Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Core adapter (CQRS pipeline + Hexaqual runner)
|
|
23
|
+
pip install hexastack-qual
|
|
24
|
+
|
|
25
|
+
# With Model Context Protocol (MCP) server support
|
|
26
|
+
pip install "hexastack-qual[mcp]"
|
|
27
|
+
|
|
28
|
+
# With NiceGUI DevTools console tab
|
|
29
|
+
pip install "hexastack-qual[ui]"
|
|
30
|
+
|
|
31
|
+
# With distributed event publishing
|
|
32
|
+
pip install "hexastack-qual[events]"
|
|
33
|
+
|
|
34
|
+
# All extras
|
|
35
|
+
pip install "hexastack-qual[all]"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 🚀 Quickstart
|
|
41
|
+
|
|
42
|
+
### 1. CQRS Pipeline Execution
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from hexastack_cqrs.infra.pipeline import ExecutionPipeline
|
|
46
|
+
from hexastack_qual.adapters.cqrs.commands import RunSanityCheckCommand
|
|
47
|
+
from hexastack_qual.adapters.cqrs.queries import GetQualityScorecardQuery
|
|
48
|
+
|
|
49
|
+
# Dispatch queries and commands via the CQRS pipeline
|
|
50
|
+
scorecard = await pipeline.execute(GetQualityScorecardQuery(package="core"))
|
|
51
|
+
print(
|
|
52
|
+
f"Healthy: {scorecard.is_healthy}, Violations: {len(scorecard.complexity_violations)}"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
result = await pipeline.execute(RunSanityCheckCommand(package="core", skip_tests=True))
|
|
56
|
+
print(f"Sanity status: {result.success}")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 2. Exposing Quality over MCP
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from hexastack_qual.adapters.mcp import get_quality_tools
|
|
63
|
+
|
|
64
|
+
# Attach quality inspection tools to an AI agent
|
|
65
|
+
agent = create_cqrs_agent(
|
|
66
|
+
pipeline=pipeline,
|
|
67
|
+
tools=get_quality_tools(),
|
|
68
|
+
system_prompt="You are an autonomous refactoring agent with self-verification tools.",
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 🏛️ Architecture
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
hexastack_qual/
|
|
78
|
+
├── domain/ # Pure models (QualityScorecard, ComplexityMetric), exceptions, and events
|
|
79
|
+
├── ports/ # QualityAuditorPort, MutationInspectorPort, PrDiagnosticPort
|
|
80
|
+
├── adapters/ # Hexaqual runner, CQRS handlers, MCP tools, NiceGUI panel, Events publisher
|
|
81
|
+
└── infra/ # QualBootstrapper (DI container registration) and Typer CLI
|
|
82
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "hexastack-qual"
|
|
3
|
+
version = "0.7.0"
|
|
4
|
+
description = "Hexagonal quality, governance, and release adapter integrating hexaqual with CQRS, MCP, UI, and Events"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "Apache-2.0"
|
|
7
|
+
requires-python = ">=3.13"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"hexaqual[all]>=0.9.0",
|
|
10
|
+
"hexastack-core",
|
|
11
|
+
"hexastack-cqrs",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[project.authors]]
|
|
15
|
+
name = "Richard West"
|
|
16
|
+
email = "dopplereffect.us@gmail.com"
|
|
17
|
+
|
|
18
|
+
[project.optional-dependencies]
|
|
19
|
+
events = ["hexastack-events"]
|
|
20
|
+
mcp = ["hexastack-mcp"]
|
|
21
|
+
ui = ["hexastack-ui"]
|
|
22
|
+
all = ["hexastack-qual[events,mcp,ui]"]
|
|
23
|
+
|
|
24
|
+
[project.entry-points."hexastack.bootstrappers"]
|
|
25
|
+
qual = "hexastack_qual.infra.bootstrap:QualBootstrapper"
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["uv_build>=0.12.3,<0.13.0"]
|
|
29
|
+
build-backend = "uv_build"
|
|
30
|
+
|
|
31
|
+
[tool.uv.sources.hexastack-core]
|
|
32
|
+
workspace = true
|
|
33
|
+
|
|
34
|
+
[tool.uv.sources.hexastack-cqrs]
|
|
35
|
+
workspace = true
|
|
36
|
+
|
|
37
|
+
[tool.uv.sources.hexastack-events]
|
|
38
|
+
workspace = true
|
|
39
|
+
|
|
40
|
+
[tool.uv.sources.hexastack-mcp]
|
|
41
|
+
workspace = true
|
|
42
|
+
|
|
43
|
+
[tool.uv.sources.hexastack-ui]
|
|
44
|
+
workspace = true
|
|
45
|
+
|
|
46
|
+
[tool.importlinter]
|
|
47
|
+
root_packages = ["hexastack_qual"]
|
|
48
|
+
|
|
49
|
+
[[tool.importlinter.contracts]]
|
|
50
|
+
name = "Hexagonal architecture layer hierarchy"
|
|
51
|
+
type = "layers"
|
|
52
|
+
containers = ["hexastack_qual"]
|
|
53
|
+
layers = [
|
|
54
|
+
"infra",
|
|
55
|
+
"adapters",
|
|
56
|
+
"ports",
|
|
57
|
+
"domain",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[tool.mutmut]
|
|
61
|
+
runner = 'pytest -x --assert=plain -o addopts=""'
|
|
62
|
+
paths_to_mutate = "src"
|
|
63
|
+
|
|
64
|
+
[tool.pytest-gremlins]
|
|
65
|
+
paths = ["src"]
|
|
66
|
+
operators = [
|
|
67
|
+
"arithmetic",
|
|
68
|
+
"boolean",
|
|
69
|
+
"boundary",
|
|
70
|
+
"comparison",
|
|
71
|
+
"return",
|
|
72
|
+
]
|
|
73
|
+
workers = "auto"
|
|
74
|
+
batch_size = 10
|
|
75
|
+
cache = true
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "hexastack-qual"
|
|
3
|
+
version = "0.7.0"
|
|
4
|
+
description = "Hexagonal quality, governance, and release adapter integrating hexaqual with CQRS, MCP, UI, and Events"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "Apache-2.0"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Richard West", email = "dopplereffect.us@gmail.com" }
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"hexaqual[all]>=0.9.0",
|
|
13
|
+
"hexastack-core",
|
|
14
|
+
"hexastack-cqrs",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.optional-dependencies]
|
|
18
|
+
events = [
|
|
19
|
+
"hexastack-events",
|
|
20
|
+
]
|
|
21
|
+
mcp = [
|
|
22
|
+
"hexastack-mcp",
|
|
23
|
+
]
|
|
24
|
+
ui = [
|
|
25
|
+
"hexastack-ui",
|
|
26
|
+
]
|
|
27
|
+
all = [
|
|
28
|
+
"hexastack-qual[events,mcp,ui]",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.entry-points."hexastack.bootstrappers"]
|
|
32
|
+
qual = "hexastack_qual.infra.bootstrap:QualBootstrapper"
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["uv_build>=0.12.3,<0.13.0"]
|
|
36
|
+
build-backend = "uv_build"
|
|
37
|
+
|
|
38
|
+
[tool.uv.sources]
|
|
39
|
+
hexastack-core = { workspace = true }
|
|
40
|
+
hexastack-cqrs = { workspace = true }
|
|
41
|
+
hexastack-events = { workspace = true }
|
|
42
|
+
hexastack-mcp = { workspace = true }
|
|
43
|
+
hexastack-ui = { workspace = true }
|
|
44
|
+
|
|
45
|
+
[tool.importlinter]
|
|
46
|
+
root_packages = ["hexastack_qual"]
|
|
47
|
+
|
|
48
|
+
[[tool.importlinter.contracts]]
|
|
49
|
+
name = "Hexagonal architecture layer hierarchy"
|
|
50
|
+
type = "layers"
|
|
51
|
+
containers = ["hexastack_qual"]
|
|
52
|
+
layers = [
|
|
53
|
+
"infra",
|
|
54
|
+
"adapters",
|
|
55
|
+
"ports",
|
|
56
|
+
"domain",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
[tool.mutmut]
|
|
60
|
+
runner = "pytest -x --assert=plain -o addopts=\"\""
|
|
61
|
+
paths_to_mutate = "src"
|
|
62
|
+
|
|
63
|
+
[tool.pytest-gremlins]
|
|
64
|
+
paths = ["src"]
|
|
65
|
+
operators = ["arithmetic", "boolean", "boundary", "comparison", "return"]
|
|
66
|
+
workers = "auto"
|
|
67
|
+
batch_size = 10
|
|
68
|
+
cache = true
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Hexastack Quality & Governance Adapter.
|
|
2
|
+
|
|
3
|
+
Bridges the Hexaqual engine into Hexastack applications, providing CQRS
|
|
4
|
+
dispatching, MCP server tools, reactive DevTools panels, and distributed event
|
|
5
|
+
publishing.
|
|
6
|
+
|
|
7
|
+
Notes/Architectural Intent:
|
|
8
|
+
Serves as the root package for hexastack-qual. Exports package version
|
|
9
|
+
and core domain models while preserving lazy loading for optional extras.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
__version__ = "0.7.0"
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"__version__",
|
|
18
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Adapters for hexastack-qual.
|
|
2
|
+
|
|
3
|
+
Notes/Architectural Intent:
|
|
4
|
+
Houses concrete adapters for Hexaqual engine, CQRS message handlers, MCP
|
|
5
|
+
tool endpoints, NiceGUI DevTools panels, and distributed event publishers.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from hexastack_qual.adapters.hexaqual.runner import HexaqualRunnerAdapter
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"HexaqualRunnerAdapter",
|
|
14
|
+
]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""CQRS messages and handlers for hexastack-qual.
|
|
2
|
+
|
|
3
|
+
Notes/Architectural Intent:
|
|
4
|
+
Exports Commands, Queries, and corresponding Handlers for dispatching
|
|
5
|
+
quality audits through the Hexastack ExecutionPipeline.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from hexastack_qual.adapters.cqrs.commands import (
|
|
11
|
+
FormatStatementsCommand,
|
|
12
|
+
RunMutationTestingCommand,
|
|
13
|
+
RunSanityCheckCommand,
|
|
14
|
+
SyncAgentAssetsCommand,
|
|
15
|
+
)
|
|
16
|
+
from hexastack_qual.adapters.cqrs.handlers import (
|
|
17
|
+
FormatStatementsHandler,
|
|
18
|
+
GetPrHealthHandler,
|
|
19
|
+
GetQualityScorecardHandler,
|
|
20
|
+
GetTestImpactHandler,
|
|
21
|
+
InspectMutantsHandler,
|
|
22
|
+
RunMutationTestingHandler,
|
|
23
|
+
RunSanityCheckHandler,
|
|
24
|
+
SyncAgentAssetsHandler,
|
|
25
|
+
)
|
|
26
|
+
from hexastack_qual.adapters.cqrs.queries import (
|
|
27
|
+
GetPrHealthQuery,
|
|
28
|
+
GetQualityScorecardQuery,
|
|
29
|
+
GetTestImpactQuery,
|
|
30
|
+
InspectMutantsQuery,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"FormatStatementsCommand",
|
|
35
|
+
"FormatStatementsHandler",
|
|
36
|
+
"GetPrHealthHandler",
|
|
37
|
+
"GetPrHealthQuery",
|
|
38
|
+
"GetQualityScorecardHandler",
|
|
39
|
+
"GetQualityScorecardQuery",
|
|
40
|
+
"GetTestImpactHandler",
|
|
41
|
+
"GetTestImpactQuery",
|
|
42
|
+
"InspectMutantsHandler",
|
|
43
|
+
"InspectMutantsQuery",
|
|
44
|
+
"RunMutationTestingCommand",
|
|
45
|
+
"RunMutationTestingHandler",
|
|
46
|
+
"RunSanityCheckCommand",
|
|
47
|
+
"RunSanityCheckHandler",
|
|
48
|
+
"SyncAgentAssetsCommand",
|
|
49
|
+
"SyncAgentAssetsHandler",
|
|
50
|
+
]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""CQRS commands for hexastack-qual.
|
|
2
|
+
|
|
3
|
+
Notes/Architectural Intent:
|
|
4
|
+
Encapsulates state-changing intent: running sanity gates, auto-fixing
|
|
5
|
+
statements, executing mutation tests, and syncing agent assets.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pydantic import ConfigDict, Field
|
|
11
|
+
|
|
12
|
+
from hexastack_core.domain import Command
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class RunSanityCheckCommand(Command):
|
|
16
|
+
"""Command to execute full or scoped sanity check quality gate.
|
|
17
|
+
|
|
18
|
+
Notes/Architectural Intent:
|
|
19
|
+
Triggers cognitive complexity, test parity, and __all__ validation.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
model_config = ConfigDict(frozen=True)
|
|
23
|
+
|
|
24
|
+
package: str | None = Field(
|
|
25
|
+
default=None,
|
|
26
|
+
description="Target package name or None for workspace.",
|
|
27
|
+
)
|
|
28
|
+
skip_tests: bool = Field(
|
|
29
|
+
default=True,
|
|
30
|
+
description="Whether to skip unit test suites.",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class FormatStatementsCommand(Command):
|
|
35
|
+
"""Command to auto-format and sort __all__ export statements.
|
|
36
|
+
|
|
37
|
+
Notes/Architectural Intent:
|
|
38
|
+
Modifies target files in-place to enforce casefold ordering.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
model_config = ConfigDict(frozen=True)
|
|
42
|
+
|
|
43
|
+
package: str | None = Field(
|
|
44
|
+
default=None,
|
|
45
|
+
description="Target package name or None for workspace.",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class RunMutationTestingCommand(Command):
|
|
50
|
+
"""Command to execute mutation testing across target components.
|
|
51
|
+
|
|
52
|
+
Notes/Architectural Intent:
|
|
53
|
+
Executes mutmut runner and compiles surviving mutant scorecards.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
model_config = ConfigDict(frozen=True)
|
|
57
|
+
|
|
58
|
+
package: str | None = Field(
|
|
59
|
+
default=None,
|
|
60
|
+
description="Target package name or None for workspace.",
|
|
61
|
+
)
|
|
62
|
+
reset: bool = Field(
|
|
63
|
+
default=False,
|
|
64
|
+
description="Whether to clear existing mutation cache before running.",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class SyncAgentAssetsCommand(Command):
|
|
69
|
+
"""Command to re-synchronize universal AI agent rules and workflows.
|
|
70
|
+
|
|
71
|
+
Notes/Architectural Intent:
|
|
72
|
+
Materializes latest hexaqual agent assets into .agents/.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
model_config = ConfigDict(frozen=True)
|
|
76
|
+
|
|
77
|
+
dry_run: bool = Field(
|
|
78
|
+
default=False,
|
|
79
|
+
description="If True, verifies sync without writing files.",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
__all__ = [
|
|
84
|
+
"FormatStatementsCommand",
|
|
85
|
+
"RunMutationTestingCommand",
|
|
86
|
+
"RunSanityCheckCommand",
|
|
87
|
+
"SyncAgentAssetsCommand",
|
|
88
|
+
]
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""CQRS command and query handlers for hexastack-qual.
|
|
2
|
+
|
|
3
|
+
Notes/Architectural Intent:
|
|
4
|
+
Connects CQRS message dispatching to QualityAuditorPort, MutationInspectorPort,
|
|
5
|
+
and PrDiagnosticPort, with optional event emission across EventBusPort.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from hexaqual.adapters.agents import FileSystemAgentAssetAdapter
|
|
13
|
+
from hexaqual.adapters.workspace import get_repo_root
|
|
14
|
+
|
|
15
|
+
from hexastack_qual.adapters.cqrs.commands import (
|
|
16
|
+
FormatStatementsCommand,
|
|
17
|
+
RunMutationTestingCommand,
|
|
18
|
+
RunSanityCheckCommand,
|
|
19
|
+
SyncAgentAssetsCommand,
|
|
20
|
+
)
|
|
21
|
+
from hexastack_qual.adapters.cqrs.queries import (
|
|
22
|
+
GetPrHealthQuery,
|
|
23
|
+
GetQualityScorecardQuery,
|
|
24
|
+
GetTestImpactQuery,
|
|
25
|
+
InspectMutantsQuery,
|
|
26
|
+
)
|
|
27
|
+
from hexastack_qual.domain.events import (
|
|
28
|
+
QualityGateFailedEvent,
|
|
29
|
+
StatementsFixedEvent,
|
|
30
|
+
)
|
|
31
|
+
from hexastack_qual.domain.models import (
|
|
32
|
+
MutantReport,
|
|
33
|
+
PrHealthSummary,
|
|
34
|
+
QualityScorecard,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if TYPE_CHECKING:
|
|
38
|
+
from hexastack_cqrs.ports.buses import EventBusPort
|
|
39
|
+
from hexastack_qual.ports.auditor import QualityAuditorPort
|
|
40
|
+
from hexastack_qual.ports.diagnostics import PrDiagnosticPort
|
|
41
|
+
from hexastack_qual.ports.mutator import MutationInspectorPort
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class RunSanityCheckHandler:
|
|
45
|
+
"""Handler for RunSanityCheckCommand."""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
auditor: QualityAuditorPort,
|
|
50
|
+
event_bus: EventBusPort | None = None,
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Initialize handler with auditor port and optional event bus."""
|
|
53
|
+
self._auditor = auditor
|
|
54
|
+
self._event_bus = event_bus
|
|
55
|
+
|
|
56
|
+
async def __call__(self, command: RunSanityCheckCommand) -> QualityScorecard:
|
|
57
|
+
"""Execute sanity check and emit failure event if unhealthy."""
|
|
58
|
+
scorecard = self._auditor.run_sanity(
|
|
59
|
+
package=command.package,
|
|
60
|
+
skip_tests=command.skip_tests,
|
|
61
|
+
)
|
|
62
|
+
if not scorecard.is_healthy and self._event_bus:
|
|
63
|
+
failed_names = [
|
|
64
|
+
c.check_name for c in scorecard.checks if c.status == "fail"
|
|
65
|
+
]
|
|
66
|
+
event = QualityGateFailedEvent(
|
|
67
|
+
target=scorecard.target,
|
|
68
|
+
failed_checks=failed_names,
|
|
69
|
+
)
|
|
70
|
+
await self._event_bus.publish(event)
|
|
71
|
+
return scorecard
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class FormatStatementsHandler:
|
|
75
|
+
"""Handler for FormatStatementsCommand."""
|
|
76
|
+
|
|
77
|
+
def __init__(
|
|
78
|
+
self,
|
|
79
|
+
auditor: QualityAuditorPort,
|
|
80
|
+
event_bus: EventBusPort | None = None,
|
|
81
|
+
) -> None:
|
|
82
|
+
"""Initialize handler with auditor port and optional event bus."""
|
|
83
|
+
self._auditor = auditor
|
|
84
|
+
self._event_bus = event_bus
|
|
85
|
+
|
|
86
|
+
async def __call__(self, command: FormatStatementsCommand) -> int:
|
|
87
|
+
"""Fix statement formatting and emit event if files modified."""
|
|
88
|
+
modified_count = self._auditor.fix_statements(package=command.package)
|
|
89
|
+
if modified_count > 0 and self._event_bus:
|
|
90
|
+
event = StatementsFixedEvent(
|
|
91
|
+
target=command.package or "workspace",
|
|
92
|
+
files_modified=modified_count,
|
|
93
|
+
)
|
|
94
|
+
await self._event_bus.publish(event)
|
|
95
|
+
return modified_count
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class RunMutationTestingHandler:
|
|
99
|
+
"""Handler for RunMutationTestingCommand."""
|
|
100
|
+
|
|
101
|
+
def __init__(self, mutator: MutationInspectorPort) -> None:
|
|
102
|
+
"""Initialize handler with mutation inspector port."""
|
|
103
|
+
self._mutator = mutator
|
|
104
|
+
|
|
105
|
+
async def __call__(self, command: RunMutationTestingCommand) -> MutantReport:
|
|
106
|
+
"""Execute mutation testing runner."""
|
|
107
|
+
return self._mutator.run_mutation_testing(
|
|
108
|
+
package=command.package,
|
|
109
|
+
reset=command.reset,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class SyncAgentAssetsHandler:
|
|
114
|
+
"""Handler for SyncAgentAssetsCommand."""
|
|
115
|
+
|
|
116
|
+
async def __call__(self, command: SyncAgentAssetsCommand) -> int:
|
|
117
|
+
"""Synchronize universal AI agent rules and workflows."""
|
|
118
|
+
root = get_repo_root()
|
|
119
|
+
adapter = FileSystemAgentAssetAdapter()
|
|
120
|
+
res = adapter.sync_assets(root, dry_run=command.dry_run)
|
|
121
|
+
return res.created_count + res.updated_count
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class GetQualityScorecardHandler:
|
|
125
|
+
"""Handler for GetQualityScorecardQuery."""
|
|
126
|
+
|
|
127
|
+
def __init__(self, auditor: QualityAuditorPort) -> None:
|
|
128
|
+
"""Initialize handler with auditor port."""
|
|
129
|
+
self._auditor = auditor
|
|
130
|
+
|
|
131
|
+
async def __call__(self, query: GetQualityScorecardQuery) -> QualityScorecard:
|
|
132
|
+
"""Execute sanity check and return scorecard."""
|
|
133
|
+
return self._auditor.run_sanity(
|
|
134
|
+
package=query.package,
|
|
135
|
+
skip_tests=query.skip_tests,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class InspectMutantsHandler:
|
|
140
|
+
"""Handler for InspectMutantsQuery."""
|
|
141
|
+
|
|
142
|
+
def __init__(self, mutator: MutationInspectorPort) -> None:
|
|
143
|
+
"""Initialize handler with mutator port."""
|
|
144
|
+
self._mutator = mutator
|
|
145
|
+
|
|
146
|
+
async def __call__(self, query: InspectMutantsQuery) -> MutantReport:
|
|
147
|
+
"""Inspect surviving mutants."""
|
|
148
|
+
return self._mutator.inspect_surviving_mutants(
|
|
149
|
+
package=query.package,
|
|
150
|
+
actionable_only=query.actionable_only,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class GetTestImpactHandler:
|
|
155
|
+
"""Handler for GetTestImpactQuery."""
|
|
156
|
+
|
|
157
|
+
def __init__(self, diagnostics: PrDiagnosticPort) -> None:
|
|
158
|
+
"""Initialize handler with diagnostics port."""
|
|
159
|
+
self._diagnostics = diagnostics
|
|
160
|
+
|
|
161
|
+
async def __call__(self, query: GetTestImpactQuery) -> list[str]:
|
|
162
|
+
"""Compute impacted test suites."""
|
|
163
|
+
return self._diagnostics.get_test_impact(base_ref=query.base_ref)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class GetPrHealthHandler:
|
|
167
|
+
"""Handler for GetPrHealthQuery."""
|
|
168
|
+
|
|
169
|
+
def __init__(self, diagnostics: PrDiagnosticPort) -> None:
|
|
170
|
+
"""Initialize handler with diagnostics port."""
|
|
171
|
+
self._diagnostics = diagnostics
|
|
172
|
+
|
|
173
|
+
async def __call__(self, query: GetPrHealthQuery) -> PrHealthSummary:
|
|
174
|
+
"""Inspect pull request diagnostic health."""
|
|
175
|
+
return self._diagnostics.get_pr_health(pr_number=query.pr_number)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
__all__ = [
|
|
179
|
+
"FormatStatementsHandler",
|
|
180
|
+
"GetPrHealthHandler",
|
|
181
|
+
"GetQualityScorecardHandler",
|
|
182
|
+
"GetTestImpactHandler",
|
|
183
|
+
"InspectMutantsHandler",
|
|
184
|
+
"RunMutationTestingHandler",
|
|
185
|
+
"RunSanityCheckHandler",
|
|
186
|
+
"SyncAgentAssetsHandler",
|
|
187
|
+
]
|