specx 0.0.0a1__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 (46) hide show
  1. specx-0.0.0a1/LICENSE.md +21 -0
  2. specx-0.0.0a1/PKG-INFO +228 -0
  3. specx-0.0.0a1/README.md +207 -0
  4. specx-0.0.0a1/pyproject.toml +84 -0
  5. specx-0.0.0a1/src/specx/__init__.py +1 -0
  6. specx-0.0.0a1/src/specx/_internal/__init__.py +1 -0
  7. specx-0.0.0a1/src/specx/_internal/exceptions.py +5 -0
  8. specx-0.0.0a1/src/specx/_internal/python_ast/__init__.py +1 -0
  9. specx-0.0.0a1/src/specx/_internal/python_ast/scanner.py +84 -0
  10. specx-0.0.0a1/src/specx/foundation/__init__.py +1 -0
  11. specx-0.0.0a1/src/specx/foundation/capability.py +9 -0
  12. specx-0.0.0a1/src/specx/foundation/command.py +16 -0
  13. specx-0.0.0a1/src/specx/foundation/configurator.py +8 -0
  14. specx-0.0.0a1/src/specx/foundation/delivery/__init__.py +1 -0
  15. specx-0.0.0a1/src/specx/foundation/delivery/controller.py +18 -0
  16. specx-0.0.0a1/src/specx/foundation/delivery/fastapi/__init__.py +1 -0
  17. specx-0.0.0a1/src/specx/foundation/delivery/fastapi/schema.py +15 -0
  18. specx-0.0.0a1/src/specx/foundation/delivery/service.py +8 -0
  19. specx-0.0.0a1/src/specx/foundation/dto.py +15 -0
  20. specx-0.0.0a1/src/specx/foundation/effect_service.py +12 -0
  21. specx-0.0.0a1/src/specx/foundation/entity.py +15 -0
  22. specx-0.0.0a1/src/specx/foundation/enums.py +10 -0
  23. specx-0.0.0a1/src/specx/foundation/exceptions.py +16 -0
  24. specx-0.0.0a1/src/specx/foundation/factory.py +8 -0
  25. specx-0.0.0a1/src/specx/foundation/gateway.py +9 -0
  26. specx-0.0.0a1/src/specx/foundation/infrastructure/__init__.py +1 -0
  27. specx-0.0.0a1/src/specx/foundation/infrastructure/sqlalchemy/__init__.py +1 -0
  28. specx-0.0.0a1/src/specx/foundation/infrastructure/sqlalchemy/model.py +23 -0
  29. specx-0.0.0a1/src/specx/foundation/pure_service.py +12 -0
  30. specx-0.0.0a1/src/specx/foundation/query.py +16 -0
  31. specx-0.0.0a1/src/specx/foundation/read_service.py +12 -0
  32. specx-0.0.0a1/src/specx/foundation/repository.py +8 -0
  33. specx-0.0.0a1/src/specx/foundation/settings.py +17 -0
  34. specx-0.0.0a1/src/specx/foundation/unit_of_work.py +7 -0
  35. specx-0.0.0a1/src/specx/foundation/unit_of_work_manager.py +29 -0
  36. specx-0.0.0a1/src/specx/foundation/use_case.py +8 -0
  37. specx-0.0.0a1/src/specx/py.typed +1 -0
  38. specx-0.0.0a1/src/specx/testing/__init__.py +1 -0
  39. specx-0.0.0a1/src/specx/testing/architecture/__init__.py +28 -0
  40. specx-0.0.0a1/src/specx/testing/architecture/checker.py +40 -0
  41. specx-0.0.0a1/src/specx/testing/architecture/context.py +722 -0
  42. specx-0.0.0a1/src/specx/testing/architecture/models.py +102 -0
  43. specx-0.0.0a1/src/specx/testing/architecture/registry.py +55 -0
  44. specx-0.0.0a1/src/specx/testing/architecture/rule.py +19 -0
  45. specx-0.0.0a1/src/specx/testing/architecture/rule_id.py +68 -0
  46. specx-0.0.0a1/src/specx/testing/architecture/rules.py +1951 -0
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Maksim Zayats
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
specx-0.0.0a1/PKG-INFO ADDED
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: specx
3
+ Version: 0.0.0a1
4
+ Summary: Rule-based architecture guardrails for Specx Python services.
5
+ License-Expression: MIT
6
+ License-File: LICENSE.md
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Framework :: Pytest
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Typing :: Typed
16
+ Requires-Dist: pydantic>=2.13.0
17
+ Requires-Dist: pydantic-settings>=2.14.0
18
+ Requires-Dist: sqlalchemy>=2.0.0
19
+ Requires-Python: >=3.11
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Specx
23
+
24
+ **Codex skills for building Python services with explicit architectural boundaries.**
25
+
26
+ Specx is a skill catalog for generating and evolving backend services that keep
27
+ application code readable under agent-driven development. It gives Codex a
28
+ shared vocabulary for packaged foundation bases, scoped core packages, delivery
29
+ adapters, unit-of-work lifecycles, dependency injection, migrations, and
30
+ architecture tests.
31
+
32
+ [Install](#install) ·
33
+ [Skills](#skills) ·
34
+ [Generated Architecture](#generated-architecture) ·
35
+ [Contribute](CONTRIBUTING.md)
36
+
37
+ ## Key Features
38
+
39
+ - **Skill-based service scaffolding.** Specx replaces a one-off code template
40
+ with composable skills for project structure, foundation usage, tooling,
41
+ DI, use cases, services, delivery controllers, infrastructure adapters,
42
+ settings, migrations, and tests.
43
+ - **Explicit class boundaries.** Generated services use class-based use cases,
44
+ services, controllers, repositories, gateways, capabilities, DTOs, entities,
45
+ units of work, and factories with packaged `specx.foundation` bases.
46
+ - **Clear transaction ownership.** Use cases open `UnitOfWorkManager` scopes.
47
+ Read/effect services may use an active UoW passed by the use case, but they do
48
+ not own transaction lifecycle.
49
+ - **Guardrails for agent work.** The `specx` Python package ships rule-based
50
+ architecture tests that reject layer leaks, entity returns from use cases,
51
+ schema bootstrap calls, bare classes, wrong suffixes, and hidden transaction
52
+ ownership.
53
+ - **Reusable runtime bases.** Generated services import small base classes from
54
+ `specx.foundation` instead of vendoring a foundation tree. Projects add
55
+ local `foundation/` modules only when a real missing category needs one.
56
+ - **Alembic-first persistence.** SQLAlchemy projects use real Alembic
57
+ migrations and drift checks instead of `metadata.create_all()` bootstraps.
58
+
59
+ ## Install
60
+
61
+ Install every Specx skill for Codex:
62
+
63
+ ```sh
64
+ npx skills add maksimzayats/specx --skill '*' --agent codex -y
65
+ ```
66
+
67
+ List skills from a local checkout:
68
+
69
+ ```sh
70
+ npx skills add . --list --full-depth
71
+ ```
72
+
73
+ Validate the catalog:
74
+
75
+ ```sh
76
+ make check
77
+ ```
78
+
79
+ Use the architecture package from generated projects:
80
+
81
+ ```python
82
+ from pathlib import Path
83
+
84
+ from specx.testing.architecture import SpecxArchitectureConfig, assert_specx_architecture
85
+
86
+
87
+ def test_specx_architecture() -> None:
88
+ assert_specx_architecture(
89
+ SpecxArchitectureConfig(
90
+ project_root=Path(__file__).resolve().parents[3],
91
+ package_name="order_service",
92
+ )
93
+ )
94
+ ```
95
+
96
+ ## What You Get
97
+
98
+ - A reusable Codex skill catalog under `skills/`.
99
+ - A typed Python guardrail package under `src/specx/`.
100
+ - A generated reference service under `samples/task-db-service/`.
101
+ - Rule-based architecture guardrails exposed through
102
+ `specx.testing.architecture`.
103
+ - A compatibility renderer that writes the tiny generated-project pytest
104
+ wrapper with the correct package name.
105
+ - Root `AGENTS.md` guidance for agents working on this catalog.
106
+ - Generated-project `AGENTS.md` guidance that projects should carry with them.
107
+
108
+ ## Skills
109
+
110
+ - `specx-project-structure` creates the initial `core`, `delivery`,
111
+ `infrastructure`, `ioc`, optional local `foundation`, optional `shared`,
112
+ migrations, tests, and generated-project agent instructions.
113
+ - `specx-foundation` teaches packaged base usage and project-local extensions
114
+ for real missing base categories.
115
+ - `specx-project-tooling` adds `uv`, Ruff, mypy, pytest, Makefile targets, and
116
+ local validation commands.
117
+ - `specx-component-architecture` decides where code belongs across scopes,
118
+ boundaries, capabilities, gateways, DTOs, schemas, adapters, and shared code.
119
+ - `specx-diwire-composition` wires `diwire.Container`, `Injected[...]`, private
120
+ registrations, app factories, and test overrides.
121
+ - `specx-add-core-use-case` adds command/query-driven use cases that return
122
+ DTOs and own UoW scopes when persistence is needed.
123
+ - `specx-add-core-service` adds focused reusable core behavior without hiding
124
+ transaction lifecycle.
125
+ - `specx-add-infrastructure-adapter` adds repositories, gateways, UoW
126
+ implementations, SQLAlchemy, Redis, HTTP, SDK, and other technical adapters.
127
+ - `specx-sqlalchemy-migrations` adds async Alembic configuration, revisions,
128
+ migration commands, and drift tests.
129
+ - `specx-add-delivery-controller` adds top-level FastAPI controllers, schemas,
130
+ route registration, and delivery-only helpers.
131
+ - `specx-settings` adds `pydantic-settings` configuration without direct
132
+ environment reads in core code.
133
+ - `specx-tests` adds unit, integration, end-to-end, DI, migration, and
134
+ architecture tests backed by the `specx` package.
135
+
136
+ ## Generated Architecture
137
+
138
+ Specx projects import foundation bases from `specx.foundation` and organize
139
+ application code around scoped core packages:
140
+
141
+ ```text
142
+ src/<package>/
143
+ core/
144
+ <scope>/
145
+ capabilities/
146
+ dtos/
147
+ entities/
148
+ exceptions/
149
+ gateways/
150
+ repositories/
151
+ services/
152
+ use_cases/
153
+ infrastructure/
154
+ delivery/
155
+ fastapi/
156
+ __main__.py
157
+ factory.py
158
+ controllers/
159
+ schemas/
160
+ services/
161
+ infrastructure/
162
+ ioc/
163
+ shared/
164
+ migrations/
165
+ ```
166
+
167
+ `core/<scope>/delivery/` is intentionally not part of the structure. Delivery
168
+ lives at the top level, while core packages stay framework-free.
169
+
170
+ Create `src/<package>/foundation/` only when a real class category is missing
171
+ from `specx.foundation`. Local foundation module filenames are not
172
+ `base_`-prefixed, but class names stay prefixed, for example `clock.py` defines
173
+ `BaseClock`.
174
+
175
+ ## Core Rules
176
+
177
+ - Every project class inherits an explicit packaged or project-local foundation
178
+ base.
179
+ - Use cases accept exactly one same-file `Command` or `Query` and return DTOs,
180
+ not entities.
181
+ - Commands represent state-changing operations. Queries are read-only, even
182
+ when the input is empty.
183
+ - Commands, queries, DTOs, entities, and other core data classes should use
184
+ `@dataclass(frozen=True, kw_only=True, slots=True)` unless the user asks for
185
+ another model type. Keep Pydantic for delivery schemas and settings.
186
+ - Core services inherit `BasePureService`, `BaseReadService`, or
187
+ `BaseEffectService`; do not add a generic `BaseService`.
188
+ - Small injectable collaborators inherit `BaseCapability`, live under
189
+ `core/<scope>/capabilities/`, and do not pretend to be services,
190
+ repositories, gateways, helpers, or managers.
191
+ - Gateway ports inherit `BaseGateway`, live under `core/<scope>/gateways/`,
192
+ declare external effects, use business language, and do not return entities.
193
+ - Persistence use cases inject a `UnitOfWorkManager` and open an active
194
+ `UnitOfWork` inside `execute(...)`.
195
+ - Services may receive an active UoW as a method argument, but they do not open
196
+ UoW scopes, commit, or roll back.
197
+ - SQLAlchemy schema is managed by Alembic migrations, not application schema
198
+ bootstrap calls.
199
+ - `diwire.Container` belongs in `ioc`, top-level delivery factory/entrypoint
200
+ code, and tests only.
201
+
202
+ ## Reference Service
203
+
204
+ The sample service under `samples/task-db-service/` is a working generated
205
+ project used to validate the skills. It includes:
206
+
207
+ - FastAPI delivery with `task_db_service.delivery.fastapi.__main__:app`.
208
+ - Task use cases with command/query inputs and DTO outputs.
209
+ - Split pure/read/effect services.
210
+ - SQLAlchemy repositories and UoW manager.
211
+ - Alembic migrations and drift checks.
212
+ - Architecture tests that call the rule-based `specx` guardrail package.
213
+
214
+ Run it from the sample directory:
215
+
216
+ ```sh
217
+ cd samples/task-db-service
218
+ make check
219
+ ```
220
+
221
+ ## Contributing
222
+
223
+ Developer setup, architecture notes, sample regeneration expectations, and
224
+ validation workflow live in [CONTRIBUTING.md](CONTRIBUTING.md).
225
+
226
+ ## License
227
+
228
+ Specx is released under the [MIT License](LICENSE.md).
@@ -0,0 +1,207 @@
1
+ # Specx
2
+
3
+ **Codex skills for building Python services with explicit architectural boundaries.**
4
+
5
+ Specx is a skill catalog for generating and evolving backend services that keep
6
+ application code readable under agent-driven development. It gives Codex a
7
+ shared vocabulary for packaged foundation bases, scoped core packages, delivery
8
+ adapters, unit-of-work lifecycles, dependency injection, migrations, and
9
+ architecture tests.
10
+
11
+ [Install](#install) ·
12
+ [Skills](#skills) ·
13
+ [Generated Architecture](#generated-architecture) ·
14
+ [Contribute](CONTRIBUTING.md)
15
+
16
+ ## Key Features
17
+
18
+ - **Skill-based service scaffolding.** Specx replaces a one-off code template
19
+ with composable skills for project structure, foundation usage, tooling,
20
+ DI, use cases, services, delivery controllers, infrastructure adapters,
21
+ settings, migrations, and tests.
22
+ - **Explicit class boundaries.** Generated services use class-based use cases,
23
+ services, controllers, repositories, gateways, capabilities, DTOs, entities,
24
+ units of work, and factories with packaged `specx.foundation` bases.
25
+ - **Clear transaction ownership.** Use cases open `UnitOfWorkManager` scopes.
26
+ Read/effect services may use an active UoW passed by the use case, but they do
27
+ not own transaction lifecycle.
28
+ - **Guardrails for agent work.** The `specx` Python package ships rule-based
29
+ architecture tests that reject layer leaks, entity returns from use cases,
30
+ schema bootstrap calls, bare classes, wrong suffixes, and hidden transaction
31
+ ownership.
32
+ - **Reusable runtime bases.** Generated services import small base classes from
33
+ `specx.foundation` instead of vendoring a foundation tree. Projects add
34
+ local `foundation/` modules only when a real missing category needs one.
35
+ - **Alembic-first persistence.** SQLAlchemy projects use real Alembic
36
+ migrations and drift checks instead of `metadata.create_all()` bootstraps.
37
+
38
+ ## Install
39
+
40
+ Install every Specx skill for Codex:
41
+
42
+ ```sh
43
+ npx skills add maksimzayats/specx --skill '*' --agent codex -y
44
+ ```
45
+
46
+ List skills from a local checkout:
47
+
48
+ ```sh
49
+ npx skills add . --list --full-depth
50
+ ```
51
+
52
+ Validate the catalog:
53
+
54
+ ```sh
55
+ make check
56
+ ```
57
+
58
+ Use the architecture package from generated projects:
59
+
60
+ ```python
61
+ from pathlib import Path
62
+
63
+ from specx.testing.architecture import SpecxArchitectureConfig, assert_specx_architecture
64
+
65
+
66
+ def test_specx_architecture() -> None:
67
+ assert_specx_architecture(
68
+ SpecxArchitectureConfig(
69
+ project_root=Path(__file__).resolve().parents[3],
70
+ package_name="order_service",
71
+ )
72
+ )
73
+ ```
74
+
75
+ ## What You Get
76
+
77
+ - A reusable Codex skill catalog under `skills/`.
78
+ - A typed Python guardrail package under `src/specx/`.
79
+ - A generated reference service under `samples/task-db-service/`.
80
+ - Rule-based architecture guardrails exposed through
81
+ `specx.testing.architecture`.
82
+ - A compatibility renderer that writes the tiny generated-project pytest
83
+ wrapper with the correct package name.
84
+ - Root `AGENTS.md` guidance for agents working on this catalog.
85
+ - Generated-project `AGENTS.md` guidance that projects should carry with them.
86
+
87
+ ## Skills
88
+
89
+ - `specx-project-structure` creates the initial `core`, `delivery`,
90
+ `infrastructure`, `ioc`, optional local `foundation`, optional `shared`,
91
+ migrations, tests, and generated-project agent instructions.
92
+ - `specx-foundation` teaches packaged base usage and project-local extensions
93
+ for real missing base categories.
94
+ - `specx-project-tooling` adds `uv`, Ruff, mypy, pytest, Makefile targets, and
95
+ local validation commands.
96
+ - `specx-component-architecture` decides where code belongs across scopes,
97
+ boundaries, capabilities, gateways, DTOs, schemas, adapters, and shared code.
98
+ - `specx-diwire-composition` wires `diwire.Container`, `Injected[...]`, private
99
+ registrations, app factories, and test overrides.
100
+ - `specx-add-core-use-case` adds command/query-driven use cases that return
101
+ DTOs and own UoW scopes when persistence is needed.
102
+ - `specx-add-core-service` adds focused reusable core behavior without hiding
103
+ transaction lifecycle.
104
+ - `specx-add-infrastructure-adapter` adds repositories, gateways, UoW
105
+ implementations, SQLAlchemy, Redis, HTTP, SDK, and other technical adapters.
106
+ - `specx-sqlalchemy-migrations` adds async Alembic configuration, revisions,
107
+ migration commands, and drift tests.
108
+ - `specx-add-delivery-controller` adds top-level FastAPI controllers, schemas,
109
+ route registration, and delivery-only helpers.
110
+ - `specx-settings` adds `pydantic-settings` configuration without direct
111
+ environment reads in core code.
112
+ - `specx-tests` adds unit, integration, end-to-end, DI, migration, and
113
+ architecture tests backed by the `specx` package.
114
+
115
+ ## Generated Architecture
116
+
117
+ Specx projects import foundation bases from `specx.foundation` and organize
118
+ application code around scoped core packages:
119
+
120
+ ```text
121
+ src/<package>/
122
+ core/
123
+ <scope>/
124
+ capabilities/
125
+ dtos/
126
+ entities/
127
+ exceptions/
128
+ gateways/
129
+ repositories/
130
+ services/
131
+ use_cases/
132
+ infrastructure/
133
+ delivery/
134
+ fastapi/
135
+ __main__.py
136
+ factory.py
137
+ controllers/
138
+ schemas/
139
+ services/
140
+ infrastructure/
141
+ ioc/
142
+ shared/
143
+ migrations/
144
+ ```
145
+
146
+ `core/<scope>/delivery/` is intentionally not part of the structure. Delivery
147
+ lives at the top level, while core packages stay framework-free.
148
+
149
+ Create `src/<package>/foundation/` only when a real class category is missing
150
+ from `specx.foundation`. Local foundation module filenames are not
151
+ `base_`-prefixed, but class names stay prefixed, for example `clock.py` defines
152
+ `BaseClock`.
153
+
154
+ ## Core Rules
155
+
156
+ - Every project class inherits an explicit packaged or project-local foundation
157
+ base.
158
+ - Use cases accept exactly one same-file `Command` or `Query` and return DTOs,
159
+ not entities.
160
+ - Commands represent state-changing operations. Queries are read-only, even
161
+ when the input is empty.
162
+ - Commands, queries, DTOs, entities, and other core data classes should use
163
+ `@dataclass(frozen=True, kw_only=True, slots=True)` unless the user asks for
164
+ another model type. Keep Pydantic for delivery schemas and settings.
165
+ - Core services inherit `BasePureService`, `BaseReadService`, or
166
+ `BaseEffectService`; do not add a generic `BaseService`.
167
+ - Small injectable collaborators inherit `BaseCapability`, live under
168
+ `core/<scope>/capabilities/`, and do not pretend to be services,
169
+ repositories, gateways, helpers, or managers.
170
+ - Gateway ports inherit `BaseGateway`, live under `core/<scope>/gateways/`,
171
+ declare external effects, use business language, and do not return entities.
172
+ - Persistence use cases inject a `UnitOfWorkManager` and open an active
173
+ `UnitOfWork` inside `execute(...)`.
174
+ - Services may receive an active UoW as a method argument, but they do not open
175
+ UoW scopes, commit, or roll back.
176
+ - SQLAlchemy schema is managed by Alembic migrations, not application schema
177
+ bootstrap calls.
178
+ - `diwire.Container` belongs in `ioc`, top-level delivery factory/entrypoint
179
+ code, and tests only.
180
+
181
+ ## Reference Service
182
+
183
+ The sample service under `samples/task-db-service/` is a working generated
184
+ project used to validate the skills. It includes:
185
+
186
+ - FastAPI delivery with `task_db_service.delivery.fastapi.__main__:app`.
187
+ - Task use cases with command/query inputs and DTO outputs.
188
+ - Split pure/read/effect services.
189
+ - SQLAlchemy repositories and UoW manager.
190
+ - Alembic migrations and drift checks.
191
+ - Architecture tests that call the rule-based `specx` guardrail package.
192
+
193
+ Run it from the sample directory:
194
+
195
+ ```sh
196
+ cd samples/task-db-service
197
+ make check
198
+ ```
199
+
200
+ ## Contributing
201
+
202
+ Developer setup, architecture notes, sample regeneration expectations, and
203
+ validation workflow live in [CONTRIBUTING.md](CONTRIBUTING.md).
204
+
205
+ ## License
206
+
207
+ Specx is released under the [MIT License](LICENSE.md).
@@ -0,0 +1,84 @@
1
+ [project]
2
+ name = "specx"
3
+ version = "0.0.0a1"
4
+ description = "Rule-based architecture guardrails for Specx Python services."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ license-files = ["LICENSE.md"]
9
+ dependencies = [
10
+ "pydantic>=2.13.0",
11
+ "pydantic-settings>=2.14.0",
12
+ "sqlalchemy>=2.0.0",
13
+ ]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Framework :: Pytest",
17
+ "Intended Audience :: Developers",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
23
+ "Typing :: Typed",
24
+ ]
25
+
26
+ [dependency-groups]
27
+ dev = [
28
+ "mypy>=2.1.0",
29
+ "pytest>=9.1.0",
30
+ "pyright>=1.1.407",
31
+ "pyyaml>=6.0.3",
32
+ "ruff>=0.15.0",
33
+ "types-PyYAML>=6.0.12",
34
+ ]
35
+
36
+ [build-system]
37
+ requires = ["uv_build>=0.11.0,<0.12.0"]
38
+ build-backend = "uv_build"
39
+
40
+ [tool.uv.build-backend]
41
+ module-name = "specx"
42
+
43
+ [tool.pytest.ini_options]
44
+ minversion = "9.0"
45
+ addopts = "-vv --strict-config --strict-markers --import-mode=importlib"
46
+ pythonpath = ["src"]
47
+ testpaths = ["tests"]
48
+ xfail_strict = true
49
+
50
+ [tool.ruff]
51
+ target-version = "py311"
52
+ line-length = 100
53
+ src = ["src", "tests", "scripts"]
54
+
55
+ [tool.ruff.lint]
56
+ select = [
57
+ "A",
58
+ "B",
59
+ "C4",
60
+ "E",
61
+ "F",
62
+ "I",
63
+ "N",
64
+ "PIE",
65
+ "PT",
66
+ "PTH",
67
+ "RET",
68
+ "RUF",
69
+ "SIM",
70
+ "UP",
71
+ ]
72
+ ignore = []
73
+
74
+ [tool.mypy]
75
+ python_version = "3.11"
76
+ strict = true
77
+ warn_unreachable = true
78
+ extra_checks = true
79
+ mypy_path = "src"
80
+
81
+ [tool.pyright]
82
+ typeCheckingMode = "strict"
83
+ pythonVersion = "3.11"
84
+ include = ["src", "tests"]
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ class BaseSpecxError(Exception):
5
+ """Base for package errors raised by Specx internals and testing APIs."""
@@ -0,0 +1,84 @@
1
+ from __future__ import annotations
2
+
3
+ import ast
4
+ from dataclasses import dataclass
5
+ from fnmatch import fnmatch
6
+ from pathlib import Path
7
+
8
+
9
+ @dataclass(frozen=True, kw_only=True, slots=True)
10
+ class PythonSourceFile:
11
+ """Parsed Python file and its lightweight static metadata."""
12
+
13
+ path: Path
14
+ tree: ast.Module
15
+ imports: frozenset[str]
16
+ aliases: dict[str, str]
17
+
18
+
19
+ @dataclass(frozen=True, kw_only=True, slots=True)
20
+ class PythonAstProject:
21
+ """Static Python source index for a project under inspection."""
22
+
23
+ files: dict[Path, PythonSourceFile]
24
+
25
+ def source_file(self, path: Path) -> PythonSourceFile:
26
+ return self.files[path]
27
+
28
+
29
+ @dataclass(frozen=True, kw_only=True, slots=True)
30
+ class PythonAstScanner:
31
+ """Parse project Python files once so rules can share AST metadata."""
32
+
33
+ project_root: Path
34
+ excluded_patterns: tuple[str, ...] = ()
35
+
36
+ def scan(self, roots: tuple[Path, ...]) -> PythonAstProject:
37
+ files: dict[Path, PythonSourceFile] = {}
38
+ for root in roots:
39
+ if not root.exists():
40
+ continue
41
+ for path in sorted(root.rglob("*.py")):
42
+ if self._is_excluded(path):
43
+ continue
44
+ text = path.read_text(encoding="utf-8")
45
+ tree = ast.parse(text, filename=str(path))
46
+ files[path] = PythonSourceFile(
47
+ path=path,
48
+ tree=tree,
49
+ imports=_imports(tree),
50
+ aliases=_import_aliases(tree),
51
+ )
52
+ return PythonAstProject(files=files)
53
+
54
+ def _is_excluded(self, path: Path) -> bool:
55
+ relative = path.relative_to(self.project_root).as_posix()
56
+ return any(fnmatch(relative, pattern) for pattern in self.excluded_patterns)
57
+
58
+
59
+ def _imports(tree: ast.Module) -> frozenset[str]:
60
+ modules: set[str] = set()
61
+ for node in ast.walk(tree):
62
+ if isinstance(node, ast.Import):
63
+ modules.update(alias.name for alias in node.names)
64
+ elif isinstance(node, ast.ImportFrom):
65
+ module_name = "." * node.level + (node.module or "")
66
+ if module_name:
67
+ modules.add(module_name)
68
+ separator = "" if module_name == "" or module_name.endswith(".") else "."
69
+ modules.update(
70
+ f"{module_name}{separator}{alias.name}" for alias in node.names if alias.name != "*"
71
+ )
72
+ return frozenset(modules)
73
+
74
+
75
+ def _import_aliases(tree: ast.Module) -> dict[str, str]:
76
+ aliases: dict[str, str] = {}
77
+ for node in ast.walk(tree):
78
+ if isinstance(node, ast.Import):
79
+ for alias in node.names:
80
+ aliases[alias.asname or alias.name.split(".")[0]] = alias.name.split(".")[-1]
81
+ elif isinstance(node, ast.ImportFrom):
82
+ for alias in node.names:
83
+ aliases[alias.asname or alias.name] = alias.name
84
+ return aliases
@@ -0,0 +1,9 @@
1
+ class BaseCapability:
2
+ """Base for small injectable collaborators narrower than services.
3
+
4
+ Capabilities do one focused thing, may be injected or faked, and do not own
5
+ application workflows, unit-of-work scopes, repositories, or gateways.
6
+
7
+ Example:
8
+ SlugGeneratingCapability creates slugs for display labels.
9
+ """
@@ -0,0 +1,16 @@
1
+ from dataclasses import dataclass
2
+
3
+ from specx.foundation.dto import BaseDTO
4
+
5
+
6
+ @dataclass(frozen=True, kw_only=True, slots=True)
7
+ class BaseCommand(BaseDTO):
8
+ """Base for use-case inputs that request state-changing work.
9
+
10
+ Example:
11
+ from dataclasses import dataclass
12
+
13
+ @dataclass(frozen=True, kw_only=True, slots=True)
14
+ class CreateTaskCommand(BaseCommand):
15
+ title: str
16
+ """
@@ -0,0 +1,8 @@
1
+ class BaseConfigurator:
2
+ """Base for bootstrap objects that apply configuration to runtime systems.
3
+
4
+ Example:
5
+ class LoggingConfigurator(BaseConfigurator):
6
+ def configure(self) -> None:
7
+ return None
8
+ """