baobab-ai-dev-core 1.0.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.
- baobab_ai_dev_core-1.0.0/LICENSE +21 -0
- baobab_ai_dev_core-1.0.0/PKG-INFO +152 -0
- baobab_ai_dev_core-1.0.0/README.md +127 -0
- baobab_ai_dev_core-1.0.0/pyproject.toml +92 -0
- baobab_ai_dev_core-1.0.0/setup.cfg +4 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/__init__.py +27 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/__init__.py +5 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/entities/__init__.py +13 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/entities/ai_provider.py +134 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/entities/artifact.py +91 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/entities/backlog.py +171 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/entities/blocker.py +110 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/entities/feature.py +114 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/entities/job.py +215 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/entities/project.py +157 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/entities/pull_request.py +85 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/entities/quality_check.py +137 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/entities/user_story.py +114 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/entities/workflow_run.py +275 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/entities/workflow_step.py +252 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/__init__.py +35 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/ai_provider_status.py +18 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/ai_provider_type.py +16 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/artifact_type.py +12 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/blocker_severity.py +16 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/branch_type.py +16 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/job_status.py +19 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/merge_decision.py +14 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/project_status.py +16 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/pull_request_status.py +12 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/quality_check_status.py +18 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/quality_check_type.py +15 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/quality_gate_status.py +14 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/work_item_status.py +20 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/workflow_run_status.py +19 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/enums/workflow_step_status.py +19 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/exceptions/__init__.py +41 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/exceptions/baobab_ai_dev_core_error.py +14 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/exceptions/domain_validation_error.py +17 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/exceptions/hierarchy_violation_error.py +13 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/exceptions/invalid_branch_name_error.py +13 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/exceptions/invalid_identifier_error.py +15 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/exceptions/invalid_status_transition_error.py +14 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/exceptions/merge_not_allowed_error.py +13 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/exceptions/provider_unavailable_error.py +13 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/exceptions/quality_gate_failed_error.py +13 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/policies/__init__.py +7 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/policies/branch_policy.py +93 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/policies/merge_eligibility_policy.py +91 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/policies/provider_fallback_policy.py +40 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/policies/pull_request_target_policy.py +51 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/policies/quality_gate_policy.py +45 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/policies/status_transition_policy.py +159 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/policies/work_item_hierarchy_policy.py +159 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/protocols/__init__.py +1 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/protocols/ai_provider_protocol.py +25 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/protocols/backlog_repository_protocol.py +31 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/protocols/clock_protocol.py +17 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/protocols/feature_repository_protocol.py +31 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/protocols/git_client_protocol.py +37 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/protocols/project_repository_protocol.py +36 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/protocols/pull_request_client_protocol.py +38 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/protocols/quality_check_repository_protocol.py +31 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/protocols/quality_runner_protocol.py +21 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/protocols/user_story_repository_protocol.py +31 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/protocols/workflow_run_repository_protocol.py +31 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/value_objects/__init__.py +23 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/value_objects/backlog_code.py +38 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/value_objects/branch_name.py +115 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/value_objects/domain_description.py +39 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/value_objects/domain_title.py +35 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/value_objects/entity_id.py +39 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/value_objects/feature_code.py +38 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/value_objects/project_slug.py +34 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/value_objects/semantic_version.py +58 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/domain/value_objects/user_story_code.py +38 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core/py.typed +0 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core.egg-info/PKG-INFO +152 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core.egg-info/SOURCES.txt +79 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core.egg-info/dependency_links.txt +1 -0
- baobab_ai_dev_core-1.0.0/src/baobab_ai_dev_core.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 baobabgit
|
|
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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: baobab-ai-dev-core
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Noyau métier Python pur de l'écosystème baobab-ai-development : entités, objets de valeur, enums, exceptions, politiques et contrats partagés.
|
|
5
|
+
Author-email: Michel ANDRIANAIVO <patrick.andri@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/baobabgit/baobab-ai-dev-core
|
|
8
|
+
Project-URL: Repository, https://github.com/baobabgit/baobab-ai-dev-core.git
|
|
9
|
+
Keywords: baobab,domain,core,workflow,ai-development
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# baobab-ai-dev-core
|
|
27
|
+
|
|
28
|
+
Noyau métier Python pur de l'écosystème **baobab-ai-development**.
|
|
29
|
+
|
|
30
|
+
Ce module centralise le vocabulaire et les règles métier partagés par les modules
|
|
31
|
+
d'infrastructure : `database`, `documents`, `git`, `quality`, `providers`, `workflow`,
|
|
32
|
+
`API`, `CLI` et `workers`. Il ne réalise aucune I/O (réseau, disque, base de données) :
|
|
33
|
+
il modélise le domaine, valide les invariants et publie des contrats (`Protocol`) que
|
|
34
|
+
les adaptateurs externes implémentent.
|
|
35
|
+
|
|
36
|
+
## Rôle du module
|
|
37
|
+
|
|
38
|
+
`baobab-ai-dev-core` fournit :
|
|
39
|
+
|
|
40
|
+
- **Entités** — `Project`, `UserStory`, `Feature`, `Backlog`, `WorkflowRun`,
|
|
41
|
+
`WorkflowStep`, `Job`, `AiProvider`, `Blocker`, `Artifact`, `QualityCheck`,
|
|
42
|
+
`PullRequest`
|
|
43
|
+
- **Objets de valeur** — identifiants, codes métier (`US-XXX`, `FEAT-XXX`, `BL-XXX`),
|
|
44
|
+
noms de branches, titres, versions sémantiques
|
|
45
|
+
- **Enums** — statuts, types, sévérités et décisions stables du domaine
|
|
46
|
+
- **Exceptions** — hiérarchie `BaobabAiDevCoreError` avec messages exploitables
|
|
47
|
+
- **Politiques** — règles pures sans I/O (transitions, hiérarchie, branches, merge,
|
|
48
|
+
quality gate, fallback provider)
|
|
49
|
+
- **Contrats `Protocol`** — repositories et clients Git, PR, qualité, providers IA,
|
|
50
|
+
horloge injectable
|
|
51
|
+
|
|
52
|
+
Le workflow documentaire **US → FEAT → BL** et la hiérarchie Git **`main` → `us/*` →
|
|
53
|
+
`feat/*` → `bl/*`** sont modélisés et validables dans ce noyau.
|
|
54
|
+
|
|
55
|
+
## Non-objectifs
|
|
56
|
+
|
|
57
|
+
Ce package **n'implémente pas** :
|
|
58
|
+
|
|
59
|
+
- API HTTP (FastAPI, etc.)
|
|
60
|
+
- Persistance SQL (SQLAlchemy, Alembic)
|
|
61
|
+
- Appels Git ou GitHub réels
|
|
62
|
+
- Exécution de providers IA (Cursor, Claude, GPT, Codex, etc.)
|
|
63
|
+
- Runners qualité réels, parsing Markdown, file de jobs persistée, CLI
|
|
64
|
+
|
|
65
|
+
Ces responsabilités appartiennent aux modules consommateurs qui implémentent les
|
|
66
|
+
`Protocol` du domaine.
|
|
67
|
+
|
|
68
|
+
## Prérequis
|
|
69
|
+
|
|
70
|
+
- Python **>= 3.12**
|
|
71
|
+
- Dépendances runtime : **aucune** (bibliothèque standard uniquement)
|
|
72
|
+
|
|
73
|
+
## Installation locale (développement)
|
|
74
|
+
|
|
75
|
+
Depuis la racine du dépôt :
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
python -m pip install -e .
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Vérifier que le package est importable :
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
python -c "import baobab_ai_dev_core; print(baobab_ai_dev_core.__version__)"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Premiers imports
|
|
88
|
+
|
|
89
|
+
### API publique stabilisée
|
|
90
|
+
|
|
91
|
+
Les consommateurs doivent importer depuis le package racine. Seuls les symboles listés
|
|
92
|
+
dans `__all__` constituent le contrat public stable :
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
import baobab_ai_dev_core
|
|
96
|
+
|
|
97
|
+
print(baobab_ai_dev_core.__version__) # "1.0.0"
|
|
98
|
+
print(baobab_ai_dev_core.__all__) # ["__version__"]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from baobab_ai_dev_core import __version__
|
|
103
|
+
|
|
104
|
+
assert __version__ == "1.0.0"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Tout symbole absent de `__all__` est considéré **interne** et peut évoluer sans
|
|
108
|
+
préavis. Les ré-exportations publiques des briques domaine seront étendues au fur et à
|
|
109
|
+
mesure de leur stabilisation.
|
|
110
|
+
|
|
111
|
+
### Accès aux briques domaine (usage interne écosystème)
|
|
112
|
+
|
|
113
|
+
En attendant l'élargissement de `__all__`, les modules de l'écosystème importent les
|
|
114
|
+
sous-packages du domaine avec des **imports absolus** :
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from baobab_ai_dev_core.domain.entities.project import Project
|
|
118
|
+
from baobab_ai_dev_core.domain.value_objects.entity_id import EntityId
|
|
119
|
+
from baobab_ai_dev_core.domain.value_objects.project_slug import ProjectSlug
|
|
120
|
+
from baobab_ai_dev_core.domain.enums.project_status import ProjectStatus
|
|
121
|
+
from baobab_ai_dev_core.domain.policies.branch_policy import BranchPolicy
|
|
122
|
+
from baobab_ai_dev_core.domain.protocols.git_client_protocol import GitClientProtocol
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Ne pas importer depuis des chemins relatifs ; ne pas dépendre de symboles non exportés
|
|
126
|
+
via `__all__` pour une API tierce.
|
|
127
|
+
|
|
128
|
+
## Arborescence du domaine
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
src/baobab_ai_dev_core/domain/
|
|
132
|
+
entities/ # entités avec identité et transitions explicites
|
|
133
|
+
value_objects/ # objets immuables validés à la création
|
|
134
|
+
enums/ # vocabulaires stables
|
|
135
|
+
exceptions/ # erreurs métier
|
|
136
|
+
policies/ # règles pures sans I/O
|
|
137
|
+
protocols/ # contrats pour l'infrastructure
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Les tests unitaires miroirs vivent sous `tests/baobab_ai_dev_core/domain/`.
|
|
141
|
+
|
|
142
|
+
## Commandes qualité
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
python -m pytest
|
|
146
|
+
python -m ruff check .
|
|
147
|
+
python -m mypy src
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Licence
|
|
151
|
+
|
|
152
|
+
MIT — voir [LICENSE](LICENSE).
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# baobab-ai-dev-core
|
|
2
|
+
|
|
3
|
+
Noyau métier Python pur de l'écosystème **baobab-ai-development**.
|
|
4
|
+
|
|
5
|
+
Ce module centralise le vocabulaire et les règles métier partagés par les modules
|
|
6
|
+
d'infrastructure : `database`, `documents`, `git`, `quality`, `providers`, `workflow`,
|
|
7
|
+
`API`, `CLI` et `workers`. Il ne réalise aucune I/O (réseau, disque, base de données) :
|
|
8
|
+
il modélise le domaine, valide les invariants et publie des contrats (`Protocol`) que
|
|
9
|
+
les adaptateurs externes implémentent.
|
|
10
|
+
|
|
11
|
+
## Rôle du module
|
|
12
|
+
|
|
13
|
+
`baobab-ai-dev-core` fournit :
|
|
14
|
+
|
|
15
|
+
- **Entités** — `Project`, `UserStory`, `Feature`, `Backlog`, `WorkflowRun`,
|
|
16
|
+
`WorkflowStep`, `Job`, `AiProvider`, `Blocker`, `Artifact`, `QualityCheck`,
|
|
17
|
+
`PullRequest`
|
|
18
|
+
- **Objets de valeur** — identifiants, codes métier (`US-XXX`, `FEAT-XXX`, `BL-XXX`),
|
|
19
|
+
noms de branches, titres, versions sémantiques
|
|
20
|
+
- **Enums** — statuts, types, sévérités et décisions stables du domaine
|
|
21
|
+
- **Exceptions** — hiérarchie `BaobabAiDevCoreError` avec messages exploitables
|
|
22
|
+
- **Politiques** — règles pures sans I/O (transitions, hiérarchie, branches, merge,
|
|
23
|
+
quality gate, fallback provider)
|
|
24
|
+
- **Contrats `Protocol`** — repositories et clients Git, PR, qualité, providers IA,
|
|
25
|
+
horloge injectable
|
|
26
|
+
|
|
27
|
+
Le workflow documentaire **US → FEAT → BL** et la hiérarchie Git **`main` → `us/*` →
|
|
28
|
+
`feat/*` → `bl/*`** sont modélisés et validables dans ce noyau.
|
|
29
|
+
|
|
30
|
+
## Non-objectifs
|
|
31
|
+
|
|
32
|
+
Ce package **n'implémente pas** :
|
|
33
|
+
|
|
34
|
+
- API HTTP (FastAPI, etc.)
|
|
35
|
+
- Persistance SQL (SQLAlchemy, Alembic)
|
|
36
|
+
- Appels Git ou GitHub réels
|
|
37
|
+
- Exécution de providers IA (Cursor, Claude, GPT, Codex, etc.)
|
|
38
|
+
- Runners qualité réels, parsing Markdown, file de jobs persistée, CLI
|
|
39
|
+
|
|
40
|
+
Ces responsabilités appartiennent aux modules consommateurs qui implémentent les
|
|
41
|
+
`Protocol` du domaine.
|
|
42
|
+
|
|
43
|
+
## Prérequis
|
|
44
|
+
|
|
45
|
+
- Python **>= 3.12**
|
|
46
|
+
- Dépendances runtime : **aucune** (bibliothèque standard uniquement)
|
|
47
|
+
|
|
48
|
+
## Installation locale (développement)
|
|
49
|
+
|
|
50
|
+
Depuis la racine du dépôt :
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
python -m pip install -e .
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Vérifier que le package est importable :
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
python -c "import baobab_ai_dev_core; print(baobab_ai_dev_core.__version__)"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Premiers imports
|
|
63
|
+
|
|
64
|
+
### API publique stabilisée
|
|
65
|
+
|
|
66
|
+
Les consommateurs doivent importer depuis le package racine. Seuls les symboles listés
|
|
67
|
+
dans `__all__` constituent le contrat public stable :
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
import baobab_ai_dev_core
|
|
71
|
+
|
|
72
|
+
print(baobab_ai_dev_core.__version__) # "1.0.0"
|
|
73
|
+
print(baobab_ai_dev_core.__all__) # ["__version__"]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from baobab_ai_dev_core import __version__
|
|
78
|
+
|
|
79
|
+
assert __version__ == "1.0.0"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Tout symbole absent de `__all__` est considéré **interne** et peut évoluer sans
|
|
83
|
+
préavis. Les ré-exportations publiques des briques domaine seront étendues au fur et à
|
|
84
|
+
mesure de leur stabilisation.
|
|
85
|
+
|
|
86
|
+
### Accès aux briques domaine (usage interne écosystème)
|
|
87
|
+
|
|
88
|
+
En attendant l'élargissement de `__all__`, les modules de l'écosystème importent les
|
|
89
|
+
sous-packages du domaine avec des **imports absolus** :
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from baobab_ai_dev_core.domain.entities.project import Project
|
|
93
|
+
from baobab_ai_dev_core.domain.value_objects.entity_id import EntityId
|
|
94
|
+
from baobab_ai_dev_core.domain.value_objects.project_slug import ProjectSlug
|
|
95
|
+
from baobab_ai_dev_core.domain.enums.project_status import ProjectStatus
|
|
96
|
+
from baobab_ai_dev_core.domain.policies.branch_policy import BranchPolicy
|
|
97
|
+
from baobab_ai_dev_core.domain.protocols.git_client_protocol import GitClientProtocol
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Ne pas importer depuis des chemins relatifs ; ne pas dépendre de symboles non exportés
|
|
101
|
+
via `__all__` pour une API tierce.
|
|
102
|
+
|
|
103
|
+
## Arborescence du domaine
|
|
104
|
+
|
|
105
|
+
```text
|
|
106
|
+
src/baobab_ai_dev_core/domain/
|
|
107
|
+
entities/ # entités avec identité et transitions explicites
|
|
108
|
+
value_objects/ # objets immuables validés à la création
|
|
109
|
+
enums/ # vocabulaires stables
|
|
110
|
+
exceptions/ # erreurs métier
|
|
111
|
+
policies/ # règles pures sans I/O
|
|
112
|
+
protocols/ # contrats pour l'infrastructure
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Les tests unitaires miroirs vivent sous `tests/baobab_ai_dev_core/domain/`.
|
|
116
|
+
|
|
117
|
+
## Commandes qualité
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
python -m pytest
|
|
121
|
+
python -m ruff check .
|
|
122
|
+
python -m mypy src
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Licence
|
|
126
|
+
|
|
127
|
+
MIT — voir [LICENSE](LICENSE).
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "baobab-ai-dev-core"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Noyau métier Python pur de l'écosystème baobab-ai-development : entités, objets de valeur, enums, exceptions, politiques et contrats partagés."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Michel ANDRIANAIVO", email = "patrick.andri@gmail.com" }]
|
|
14
|
+
keywords = ["baobab", "domain", "core", "workflow", "ai-development"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
24
|
+
"Topic :: Software Development :: Libraries",
|
|
25
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
26
|
+
"Typing :: Typed",
|
|
27
|
+
]
|
|
28
|
+
dependencies = []
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://github.com/baobabgit/baobab-ai-dev-core"
|
|
32
|
+
Repository = "https://github.com/baobabgit/baobab-ai-dev-core.git"
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.packages.find]
|
|
35
|
+
where = ["src"]
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.package-data]
|
|
38
|
+
baobab_ai_dev_core = ["py.typed"]
|
|
39
|
+
|
|
40
|
+
[tool.pytest.ini_options]
|
|
41
|
+
minversion = "8.0"
|
|
42
|
+
testpaths = ["tests"]
|
|
43
|
+
pythonpath = ["src"]
|
|
44
|
+
addopts = "-ra --strict-markers --strict-config"
|
|
45
|
+
|
|
46
|
+
[tool.ruff]
|
|
47
|
+
line-length = 100
|
|
48
|
+
target-version = "py312"
|
|
49
|
+
src = ["src", "tests"]
|
|
50
|
+
|
|
51
|
+
[tool.ruff.lint]
|
|
52
|
+
select = [
|
|
53
|
+
"E", # pycodestyle errors
|
|
54
|
+
"W", # pycodestyle warnings
|
|
55
|
+
"F", # pyflakes
|
|
56
|
+
"I", # isort
|
|
57
|
+
"N", # pep8-naming
|
|
58
|
+
"UP", # pyupgrade
|
|
59
|
+
"B", # flake8-bugbear
|
|
60
|
+
"C4", # flake8-comprehensions
|
|
61
|
+
"SIM", # flake8-simplify
|
|
62
|
+
"PT", # flake8-pytest-style
|
|
63
|
+
"TID", # flake8-tidy-imports
|
|
64
|
+
"ANN", # flake8-annotations
|
|
65
|
+
"D", # pydocstyle
|
|
66
|
+
"PL", # pylint
|
|
67
|
+
"RUF", # ruff-specific
|
|
68
|
+
]
|
|
69
|
+
ignore = [
|
|
70
|
+
# D401 impose le « mode impératif » selon une heuristique anglophone,
|
|
71
|
+
# incompatible avec la convention de docstrings en français du projet.
|
|
72
|
+
"D401",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
[tool.ruff.lint.pydocstyle]
|
|
76
|
+
convention = "pep257"
|
|
77
|
+
|
|
78
|
+
[tool.ruff.lint.flake8-tidy-imports]
|
|
79
|
+
ban-relative-imports = "all"
|
|
80
|
+
|
|
81
|
+
[tool.ruff.lint.per-file-ignores]
|
|
82
|
+
"tests/**/*.py" = ["D101", "D102", "D103", "PLR2004"]
|
|
83
|
+
|
|
84
|
+
[tool.mypy]
|
|
85
|
+
python_version = "3.12"
|
|
86
|
+
strict = true
|
|
87
|
+
warn_unused_configs = true
|
|
88
|
+
warn_redundant_casts = true
|
|
89
|
+
warn_unused_ignores = true
|
|
90
|
+
disallow_any_generics = true
|
|
91
|
+
no_implicit_reexport = true
|
|
92
|
+
show_error_codes = true
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""baobab-ai-dev-core — noyau métier Python pur de l'écosystème baobab-ai-development.
|
|
2
|
+
|
|
3
|
+
Ce package expose les briques de domaine (entités, objets de valeur, enums,
|
|
4
|
+
exceptions, politiques et contrats ``Protocol``) consommées par les modules
|
|
5
|
+
database, documents, git, quality, providers, workflow, API, CLI et workers.
|
|
6
|
+
|
|
7
|
+
Convention d'API publique
|
|
8
|
+
--------------------------
|
|
9
|
+
- Point d'entrée unique : les consommateurs importent depuis ``baobab_ai_dev_core``
|
|
10
|
+
(``from baobab_ai_dev_core import ...``), jamais depuis les sous-modules internes.
|
|
11
|
+
- Contrat explicite : seuls les symboles listés dans :data:`__all__` font partie de
|
|
12
|
+
l'API publique stable. Tout symbole absent de ``__all__`` est considéré interne et
|
|
13
|
+
peut changer sans préavis.
|
|
14
|
+
- Compatibilité : ``__all__`` est maintenu trié pour limiter les diffs et garantir un
|
|
15
|
+
ordre déterministe. Les sous-packages du domaine (``domain.entities``,
|
|
16
|
+
``domain.enums``, ``domain.value_objects``, ``domain.policies``,
|
|
17
|
+
``domain.protocols``, ``domain.exceptions``) y seront re-exportés au fur et à mesure
|
|
18
|
+
de leur stabilisation dans les user stories suivantes.
|
|
19
|
+
|
|
20
|
+
À ce stade, seul le numéro de version est exposé publiquement via ``__all__``.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
__version__ = "1.0.0"
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"__version__",
|
|
27
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Entités métier du domaine baobab-ai-dev-core."""
|
|
2
|
+
|
|
3
|
+
from baobab_ai_dev_core.domain.entities.backlog import Backlog
|
|
4
|
+
from baobab_ai_dev_core.domain.entities.feature import Feature
|
|
5
|
+
from baobab_ai_dev_core.domain.entities.project import Project
|
|
6
|
+
from baobab_ai_dev_core.domain.entities.user_story import UserStory
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"Backlog",
|
|
10
|
+
"Feature",
|
|
11
|
+
"Project",
|
|
12
|
+
"UserStory",
|
|
13
|
+
]
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Entité métier représentant un provider IA au niveau domaine."""
|
|
2
|
+
|
|
3
|
+
from baobab_ai_dev_core.domain.enums.ai_provider_status import AiProviderStatus
|
|
4
|
+
from baobab_ai_dev_core.domain.enums.ai_provider_type import AiProviderType
|
|
5
|
+
from baobab_ai_dev_core.domain.exceptions.domain_validation_error import (
|
|
6
|
+
DomainValidationError,
|
|
7
|
+
)
|
|
8
|
+
from baobab_ai_dev_core.domain.value_objects.domain_title import DomainTitle
|
|
9
|
+
from baobab_ai_dev_core.domain.value_objects.entity_id import EntityId
|
|
10
|
+
|
|
11
|
+
_PRIORITE_MIN = 1
|
|
12
|
+
_QUOTA_OK = "ok"
|
|
13
|
+
_QUOTA_EXHAUSTED = "usage_exhausted"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _valider_priorite(priorite: int) -> int:
|
|
17
|
+
if priorite < _PRIORITE_MIN:
|
|
18
|
+
raise DomainValidationError(
|
|
19
|
+
f"La priorité doit être >= {_PRIORITE_MIN} (reçue : {priorite})."
|
|
20
|
+
)
|
|
21
|
+
return priorite
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _valider_quota_state(quota_state: str) -> str:
|
|
25
|
+
nettoye = quota_state.strip()
|
|
26
|
+
if nettoye not in {_QUOTA_OK, _QUOTA_EXHAUSTED}:
|
|
27
|
+
raise DomainValidationError(
|
|
28
|
+
f"quota_state invalide : '{quota_state}'. "
|
|
29
|
+
f"Valeurs attendues : '{_QUOTA_OK}', '{_QUOTA_EXHAUSTED}'."
|
|
30
|
+
)
|
|
31
|
+
return nettoye
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _valider_coherence_statut_quota(
|
|
35
|
+
status: AiProviderStatus,
|
|
36
|
+
quota_state: str,
|
|
37
|
+
) -> None:
|
|
38
|
+
"""Vérifie l'alignement entre statut et quota."""
|
|
39
|
+
if status is AiProviderStatus.USAGE_EXHAUSTED and quota_state != _QUOTA_EXHAUSTED:
|
|
40
|
+
raise DomainValidationError(
|
|
41
|
+
"Le statut 'usage_exhausted' exige quota_state='usage_exhausted'."
|
|
42
|
+
)
|
|
43
|
+
if status is AiProviderStatus.AVAILABLE and quota_state != _QUOTA_OK:
|
|
44
|
+
raise DomainValidationError(
|
|
45
|
+
"Le statut 'available' exige quota_state='ok'."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class AiProvider:
|
|
50
|
+
"""Provider IA métier avec priorité, statut et état de quota.
|
|
51
|
+
|
|
52
|
+
Ne contient aucun client technique : l'intégration relève du module
|
|
53
|
+
``providers``.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__( # noqa: PLR0913
|
|
57
|
+
self,
|
|
58
|
+
provider_id: EntityId,
|
|
59
|
+
name: DomainTitle,
|
|
60
|
+
provider_type: AiProviderType,
|
|
61
|
+
status: AiProviderStatus,
|
|
62
|
+
priority: int,
|
|
63
|
+
quota_state: str,
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Construit un provider en validant priorité et quota."""
|
|
66
|
+
priorite = _valider_priorite(priority)
|
|
67
|
+
quota = _valider_quota_state(quota_state)
|
|
68
|
+
_valider_coherence_statut_quota(status, quota)
|
|
69
|
+
self._provider_id = provider_id
|
|
70
|
+
self._name = name
|
|
71
|
+
self._provider_type = provider_type
|
|
72
|
+
self._status = status
|
|
73
|
+
self._priority = priorite
|
|
74
|
+
self._quota_state = quota
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def provider_id(self) -> EntityId:
|
|
78
|
+
"""Identifiant stable du provider."""
|
|
79
|
+
return self._provider_id
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def name(self) -> DomainTitle:
|
|
83
|
+
"""Nom affiché du provider."""
|
|
84
|
+
return self._name
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def provider_type(self) -> AiProviderType:
|
|
88
|
+
"""Famille technique du provider."""
|
|
89
|
+
return self._provider_type
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def status(self) -> AiProviderStatus:
|
|
93
|
+
"""Statut de disponibilité courant."""
|
|
94
|
+
return self._status
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def priority(self) -> int:
|
|
98
|
+
"""Priorité de sélection (1 = la plus haute)."""
|
|
99
|
+
return self._priority
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def quota_state(self) -> str:
|
|
103
|
+
"""État du quota (``ok`` ou ``usage_exhausted``)."""
|
|
104
|
+
return self._quota_state
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def is_selectable(self) -> bool:
|
|
108
|
+
"""Indique si le provider peut être sélectionné pour un run."""
|
|
109
|
+
return (
|
|
110
|
+
self._status is AiProviderStatus.AVAILABLE
|
|
111
|
+
and self._quota_state == _QUOTA_OK
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def mark_usage_exhausted(self) -> None:
|
|
115
|
+
"""Marque le quota comme épuisé (distinct de indisponible ou erreur)."""
|
|
116
|
+
self._status = AiProviderStatus.USAGE_EXHAUSTED
|
|
117
|
+
self._quota_state = _QUOTA_EXHAUSTED
|
|
118
|
+
|
|
119
|
+
def mark_error(self) -> None:
|
|
120
|
+
"""Marque une erreur technique sans confondre avec quota épuisé."""
|
|
121
|
+
self._status = AiProviderStatus.ERROR
|
|
122
|
+
|
|
123
|
+
def disable(self) -> None:
|
|
124
|
+
"""Désactive volontairement le provider."""
|
|
125
|
+
self._status = AiProviderStatus.DISABLED
|
|
126
|
+
|
|
127
|
+
def mark_unavailable(self) -> None:
|
|
128
|
+
"""Marque une indisponibilité ponctuelle."""
|
|
129
|
+
self._status = AiProviderStatus.UNAVAILABLE
|
|
130
|
+
|
|
131
|
+
def restore_available(self) -> None:
|
|
132
|
+
"""Restaure un provider disponible avec quota OK."""
|
|
133
|
+
self._status = AiProviderStatus.AVAILABLE
|
|
134
|
+
self._quota_state = _QUOTA_OK
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Entité métier représentant un livrable produit pendant une exécution."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
|
|
6
|
+
from baobab_ai_dev_core.domain.enums.artifact_type import ArtifactType
|
|
7
|
+
from baobab_ai_dev_core.domain.exceptions.domain_validation_error import (
|
|
8
|
+
DomainValidationError,
|
|
9
|
+
)
|
|
10
|
+
from baobab_ai_dev_core.domain.value_objects.entity_id import EntityId
|
|
11
|
+
|
|
12
|
+
_CHEMIN_RELATIF_PATTERN = re.compile(r"^[a-zA-Z0-9_./-]+$")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _valider_chemin(path: str) -> str:
|
|
16
|
+
nettoye = path.strip()
|
|
17
|
+
if not nettoye:
|
|
18
|
+
raise DomainValidationError("Le chemin d'artefact ne peut pas être vide.")
|
|
19
|
+
if nettoye.startswith("/") or ".." in nettoye.split("/"):
|
|
20
|
+
raise DomainValidationError(
|
|
21
|
+
f"Le chemin '{nettoye}' doit être relatif et ne pas contenir '..'."
|
|
22
|
+
)
|
|
23
|
+
if not _CHEMIN_RELATIF_PATTERN.fullmatch(nettoye):
|
|
24
|
+
raise DomainValidationError(
|
|
25
|
+
f"Le chemin '{nettoye}' contient des caractères non autorisés."
|
|
26
|
+
)
|
|
27
|
+
return nettoye
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _valider_checksum(checksum: str | None) -> str | None:
|
|
31
|
+
if checksum is None:
|
|
32
|
+
return None
|
|
33
|
+
nettoye = checksum.strip()
|
|
34
|
+
if not nettoye:
|
|
35
|
+
raise DomainValidationError(
|
|
36
|
+
"Le checksum ne peut pas être vide lorsqu'il est renseigné."
|
|
37
|
+
)
|
|
38
|
+
return nettoye
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Artifact:
|
|
42
|
+
"""Livrable généré pendant un workflow run (patch, rapport, fichier, etc.)."""
|
|
43
|
+
|
|
44
|
+
def __init__( # noqa: PLR0913
|
|
45
|
+
self,
|
|
46
|
+
artifact_id: EntityId,
|
|
47
|
+
workflow_run_id: EntityId,
|
|
48
|
+
artifact_type: ArtifactType,
|
|
49
|
+
path: str,
|
|
50
|
+
checksum: str | None,
|
|
51
|
+
created_at: datetime,
|
|
52
|
+
) -> None:
|
|
53
|
+
"""Construit un artefact en validant le chemin relatif et le checksum."""
|
|
54
|
+
chemin = _valider_chemin(path)
|
|
55
|
+
empreinte = _valider_checksum(checksum)
|
|
56
|
+
self._artifact_id = artifact_id
|
|
57
|
+
self._workflow_run_id = workflow_run_id
|
|
58
|
+
self._artifact_type = artifact_type
|
|
59
|
+
self._path = chemin
|
|
60
|
+
self._checksum = empreinte
|
|
61
|
+
self._created_at = created_at
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def artifact_id(self) -> EntityId:
|
|
65
|
+
"""Identifiant stable de l'artefact."""
|
|
66
|
+
return self._artifact_id
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def workflow_run_id(self) -> EntityId:
|
|
70
|
+
"""Identifiant du run ayant produit l'artefact."""
|
|
71
|
+
return self._workflow_run_id
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def artifact_type(self) -> ArtifactType:
|
|
75
|
+
"""Type métier de l'artefact."""
|
|
76
|
+
return self._artifact_type
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def path(self) -> str:
|
|
80
|
+
"""Chemin relatif validé de l'artefact."""
|
|
81
|
+
return self._path
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def checksum(self) -> str | None:
|
|
85
|
+
"""Empreinte facultative du contenu."""
|
|
86
|
+
return self._checksum
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def created_at(self) -> datetime:
|
|
90
|
+
"""Horodatage de création."""
|
|
91
|
+
return self._created_at
|