compos-cli 0.0.0__py3-none-any.whl
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.
- compos/cli/__init__.py +0 -0
- compos/cli/analyzers/__init__.py +10 -0
- compos/cli/analyzers/base.py +66 -0
- compos/cli/analyzers/docker_compose.py +137 -0
- compos/cli/analyzers/python.py +604 -0
- compos/cli/analyzers/typescript.py +823 -0
- compos/cli/git.py +92 -0
- compos/cli/main.py +464 -0
- compos/cli/watcher.py +1 -0
- compos/core/__init__.py +119 -0
- compos/core/diff.py +131 -0
- compos/core/graph.py +648 -0
- compos/core/integrity.py +346 -0
- compos/core/merge.py +289 -0
- compos/core/merge_log.py +128 -0
- compos/core/versioning.py +43 -0
- compos/core/write_pipeline.py +574 -0
- compos/schema/__init__.py +57 -0
- compos/schema/models.py +440 -0
- compos/schema/validation.py +1 -0
- compos/schema/versioning.py +1 -0
- compos/storage/__init__.py +29 -0
- compos/storage/local.py +209 -0
- compos/storage/locking.py +74 -0
- compos/storage/merge_log.py +92 -0
- compos_cli-0.0.0.dist-info/METADATA +16 -0
- compos_cli-0.0.0.dist-info/RECORD +29 -0
- compos_cli-0.0.0.dist-info/WHEEL +4 -0
- compos_cli-0.0.0.dist-info/entry_points.txt +7 -0
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
"""Write validation pipeline — ALL map mutations go through here.
|
|
2
|
+
|
|
3
|
+
Six-stage pipeline:
|
|
4
|
+
1. Schema validation (enforced by Pydantic at construction)
|
|
5
|
+
2. Provenance completeness (enforced by schema validators)
|
|
6
|
+
3. ID uniqueness (register only)
|
|
7
|
+
4. Referential integrity
|
|
8
|
+
5. Structural safety (cycle detection for relationships)
|
|
9
|
+
6. Merge engine (update only)
|
|
10
|
+
|
|
11
|
+
The pipeline is pure — it returns a WriteResult with the new map.
|
|
12
|
+
It does NOT persist. The caller is responsible for storage I/O.
|
|
13
|
+
|
|
14
|
+
Removal is provenance-gated: a caller can only remove objects created
|
|
15
|
+
by the same or lower priority source. Lower-priority callers get a
|
|
16
|
+
rejection with a warning flagging the object for human review.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from datetime import UTC, datetime
|
|
23
|
+
from enum import StrEnum
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
from compos.core.graph import evaluate_proposed_change
|
|
27
|
+
from compos.core.integrity import (
|
|
28
|
+
check_id_uniqueness,
|
|
29
|
+
check_referential_integrity,
|
|
30
|
+
check_remove_blocked,
|
|
31
|
+
)
|
|
32
|
+
from compos.core.merge import merge_object
|
|
33
|
+
from compos.core.merge_log import MergeLogEntry, create_entry
|
|
34
|
+
from compos.core.versioning import VersionBump, increment_version
|
|
35
|
+
from compos.schema.models import (
|
|
36
|
+
Component,
|
|
37
|
+
ComposMap,
|
|
38
|
+
Constraint,
|
|
39
|
+
ConstraintStatus,
|
|
40
|
+
Decision,
|
|
41
|
+
DecisionStatus,
|
|
42
|
+
ObjectStatus,
|
|
43
|
+
ProvenanceSource,
|
|
44
|
+
Relationship,
|
|
45
|
+
Risk,
|
|
46
|
+
RiskStatus,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
# Types
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
type MapObject = Component | Relationship | Constraint | Risk | Decision
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class OperationType(StrEnum):
|
|
57
|
+
REGISTER = "register"
|
|
58
|
+
UPDATE = "update"
|
|
59
|
+
REMOVE = "remove"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass(frozen=True, slots=True)
|
|
63
|
+
class WriteError:
|
|
64
|
+
"""A single validation failure."""
|
|
65
|
+
|
|
66
|
+
stage: str
|
|
67
|
+
message: str
|
|
68
|
+
details: Any = None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass(frozen=True, slots=True)
|
|
72
|
+
class WriteResult:
|
|
73
|
+
"""Outcome of a write pipeline execution."""
|
|
74
|
+
|
|
75
|
+
success: bool
|
|
76
|
+
new_map: ComposMap | None
|
|
77
|
+
new_version: int | None
|
|
78
|
+
merge_summary: MergeLogEntry | None
|
|
79
|
+
errors: tuple[WriteError, ...]
|
|
80
|
+
warnings: tuple[str, ...]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
# Collection name mapping
|
|
85
|
+
# ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
_TYPE_TO_COLLECTION: dict[type, str] = {
|
|
88
|
+
Component: "components",
|
|
89
|
+
Relationship: "relationships",
|
|
90
|
+
Constraint: "constraints",
|
|
91
|
+
Risk: "risks",
|
|
92
|
+
Decision: "decisions",
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
_TYPE_TO_NAME: dict[type, str] = {
|
|
96
|
+
Component: "component",
|
|
97
|
+
Relationship: "relationship",
|
|
98
|
+
Constraint: "constraint",
|
|
99
|
+
Risk: "risk",
|
|
100
|
+
Decision: "decision",
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# Type-aware removal: each type maps to its correct REMOVED status value
|
|
104
|
+
_TYPE_TO_REMOVED_STATUS: dict[
|
|
105
|
+
type, ObjectStatus | ConstraintStatus | RiskStatus | DecisionStatus
|
|
106
|
+
] = {
|
|
107
|
+
Component: ObjectStatus.REMOVED,
|
|
108
|
+
Relationship: ObjectStatus.REMOVED,
|
|
109
|
+
Constraint: ConstraintStatus.REMOVED,
|
|
110
|
+
Risk: RiskStatus.REMOVED,
|
|
111
|
+
Decision: DecisionStatus.REMOVED,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# Provenance priority for gating removal
|
|
115
|
+
_SOURCE_PRIORITY: dict[ProvenanceSource, int] = {
|
|
116
|
+
ProvenanceSource.STATIC_ANALYSIS: 1,
|
|
117
|
+
ProvenanceSource.AI_ANNOTATED: 2,
|
|
118
|
+
ProvenanceSource.HUMAN_INPUT: 3,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# ---------------------------------------------------------------------------
|
|
123
|
+
# Internal helpers
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _fail(stage: str, message: str, details: Any = None) -> WriteResult:
|
|
128
|
+
"""Shorthand for a failed WriteResult."""
|
|
129
|
+
return WriteResult(
|
|
130
|
+
success=False,
|
|
131
|
+
new_map=None,
|
|
132
|
+
new_version=None,
|
|
133
|
+
merge_summary=None,
|
|
134
|
+
errors=(WriteError(stage=stage, message=message, details=details),),
|
|
135
|
+
warnings=(),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _find_existing(
|
|
140
|
+
compos_map: ComposMap, obj_id: str, obj_type: type,
|
|
141
|
+
) -> MapObject | None:
|
|
142
|
+
"""Find an existing object by ID in its collection."""
|
|
143
|
+
collection = _TYPE_TO_COLLECTION[obj_type]
|
|
144
|
+
for item in getattr(compos_map, collection):
|
|
145
|
+
if item.id == obj_id:
|
|
146
|
+
return item # type: ignore[no-any-return]
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _build_map_with(
|
|
151
|
+
compos_map: ComposMap,
|
|
152
|
+
collection: str,
|
|
153
|
+
new_items: tuple[Any, ...],
|
|
154
|
+
) -> ComposMap:
|
|
155
|
+
"""Return a new ComposMap with one collection replaced."""
|
|
156
|
+
data = compos_map.model_dump(by_alias=False)
|
|
157
|
+
data[collection] = new_items
|
|
158
|
+
return ComposMap.model_validate(data)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _replace_in_collection(
|
|
162
|
+
compos_map: ComposMap,
|
|
163
|
+
obj: MapObject,
|
|
164
|
+
collection: str,
|
|
165
|
+
) -> ComposMap:
|
|
166
|
+
"""Return a new ComposMap with obj replacing the same-ID object in collection."""
|
|
167
|
+
items = getattr(compos_map, collection)
|
|
168
|
+
new_items = tuple(obj if item.id == obj.id else item for item in items)
|
|
169
|
+
return _build_map_with(compos_map, collection, new_items)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _add_to_collection(
|
|
173
|
+
compos_map: ComposMap,
|
|
174
|
+
obj: MapObject,
|
|
175
|
+
collection: str,
|
|
176
|
+
) -> ComposMap:
|
|
177
|
+
"""Return a new ComposMap with obj appended to collection."""
|
|
178
|
+
items = getattr(compos_map, collection)
|
|
179
|
+
new_items = items + (obj,)
|
|
180
|
+
return _build_map_with(compos_map, collection, new_items)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _apply_version_bump(compos_map: ComposMap, bump: VersionBump) -> ComposMap:
|
|
184
|
+
"""Return a new ComposMap with version bump applied."""
|
|
185
|
+
data = compos_map.model_dump(by_alias=False)
|
|
186
|
+
data["map_version"] = bump.new_version
|
|
187
|
+
data["generated_at"] = bump.generated_at
|
|
188
|
+
data["parent_version"] = bump.parent_version
|
|
189
|
+
if bump.git_commit is not None:
|
|
190
|
+
data["git_commit"] = bump.git_commit
|
|
191
|
+
return ComposMap.model_validate(data)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _normalize_source(source: ProvenanceSource | Any) -> str:
|
|
195
|
+
"""Normalize provenance source to string for priority lookup.
|
|
196
|
+
|
|
197
|
+
Handles both ProvenanceSource and DecisionProvenanceSource.
|
|
198
|
+
"""
|
|
199
|
+
return str(source.value) if hasattr(source, "value") else str(source)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _to_provenance_source(source_str: str) -> ProvenanceSource:
|
|
203
|
+
"""Convert a source string to ProvenanceSource enum.
|
|
204
|
+
|
|
205
|
+
Works for both ProvenanceSource and DecisionProvenanceSource values
|
|
206
|
+
since they share the same string values (minus static-analysis).
|
|
207
|
+
"""
|
|
208
|
+
return ProvenanceSource(source_str)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# ---------------------------------------------------------------------------
|
|
212
|
+
# Generic pipeline functions
|
|
213
|
+
# ---------------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _execute_register(
|
|
217
|
+
current_map: ComposMap,
|
|
218
|
+
obj: MapObject,
|
|
219
|
+
git_commit: str | None = None,
|
|
220
|
+
) -> WriteResult:
|
|
221
|
+
"""Pipeline for REGISTER operations."""
|
|
222
|
+
collection = _TYPE_TO_COLLECTION[type(obj)]
|
|
223
|
+
type_name = _TYPE_TO_NAME[type(obj)]
|
|
224
|
+
|
|
225
|
+
# Stage 3: ID uniqueness
|
|
226
|
+
uniqueness_violation = check_id_uniqueness(current_map, obj.id, collection)
|
|
227
|
+
if uniqueness_violation is not None:
|
|
228
|
+
return _fail("uniqueness", uniqueness_violation.message, uniqueness_violation)
|
|
229
|
+
|
|
230
|
+
# Stage 4: Referential integrity
|
|
231
|
+
integrity_violations = check_referential_integrity(current_map, obj)
|
|
232
|
+
if integrity_violations:
|
|
233
|
+
messages = [v.message for v in integrity_violations]
|
|
234
|
+
return _fail(
|
|
235
|
+
"integrity",
|
|
236
|
+
f"Referential integrity violations: {'; '.join(messages)}",
|
|
237
|
+
integrity_violations,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Stage 5: Structural safety (relationships only — cycle detection)
|
|
241
|
+
if isinstance(obj, Relationship):
|
|
242
|
+
eval_result = evaluate_proposed_change(
|
|
243
|
+
current_map, proposed_relationships=(obj,)
|
|
244
|
+
)
|
|
245
|
+
if eval_result.cycles:
|
|
246
|
+
cycle_desc = "; ".join(
|
|
247
|
+
f"cycle: {' -> '.join(c.component_ids)}" for c in eval_result.cycles
|
|
248
|
+
)
|
|
249
|
+
return _fail(
|
|
250
|
+
"structural",
|
|
251
|
+
f"Relationship would introduce cycle(s): {cycle_desc}",
|
|
252
|
+
eval_result.cycles,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# All checks passed — build new map
|
|
256
|
+
new_map = _add_to_collection(current_map, obj, collection)
|
|
257
|
+
|
|
258
|
+
# Version bump
|
|
259
|
+
bump = increment_version(current_map, git_commit=git_commit)
|
|
260
|
+
new_map = _apply_version_bump(new_map, bump)
|
|
261
|
+
|
|
262
|
+
# Merge log entry (no merge for register)
|
|
263
|
+
source_str = _normalize_source(obj.provenance.source)
|
|
264
|
+
log_entry = create_entry(
|
|
265
|
+
version_bump=bump,
|
|
266
|
+
operation=f"register_{type_name}",
|
|
267
|
+
target_id=obj.id,
|
|
268
|
+
target_type=type_name,
|
|
269
|
+
source=_to_provenance_source(source_str),
|
|
270
|
+
merge_result=None,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
return WriteResult(
|
|
274
|
+
success=True,
|
|
275
|
+
new_map=new_map,
|
|
276
|
+
new_version=bump.new_version,
|
|
277
|
+
merge_summary=log_entry,
|
|
278
|
+
errors=(),
|
|
279
|
+
warnings=(),
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _execute_update(
|
|
284
|
+
current_map: ComposMap,
|
|
285
|
+
obj: MapObject,
|
|
286
|
+
git_commit: str | None = None,
|
|
287
|
+
) -> WriteResult:
|
|
288
|
+
"""Pipeline for UPDATE operations."""
|
|
289
|
+
type_name = _TYPE_TO_NAME[type(obj)]
|
|
290
|
+
collection = _TYPE_TO_COLLECTION[type(obj)]
|
|
291
|
+
|
|
292
|
+
# Find existing
|
|
293
|
+
existing = _find_existing(current_map, obj.id, type(obj))
|
|
294
|
+
if existing is None:
|
|
295
|
+
return _fail("not_found", f"{type_name} '{obj.id}' not found")
|
|
296
|
+
|
|
297
|
+
# Stage 4: Referential integrity (on the incoming object)
|
|
298
|
+
integrity_violations = check_referential_integrity(current_map, obj)
|
|
299
|
+
if integrity_violations:
|
|
300
|
+
messages = [v.message for v in integrity_violations]
|
|
301
|
+
return _fail(
|
|
302
|
+
"integrity",
|
|
303
|
+
f"Referential integrity violations: {'; '.join(messages)}",
|
|
304
|
+
integrity_violations,
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
# Stage 6: Merge engine
|
|
308
|
+
merge_result = merge_object(existing, obj)
|
|
309
|
+
|
|
310
|
+
# Build new map with merged object
|
|
311
|
+
new_map = _replace_in_collection(current_map, merge_result.merged_obj, collection)
|
|
312
|
+
|
|
313
|
+
# Version bump
|
|
314
|
+
bump = increment_version(current_map, git_commit=git_commit)
|
|
315
|
+
new_map = _apply_version_bump(new_map, bump)
|
|
316
|
+
|
|
317
|
+
# Merge log entry
|
|
318
|
+
source_str = _normalize_source(obj.provenance.source)
|
|
319
|
+
log_entry = create_entry(
|
|
320
|
+
version_bump=bump,
|
|
321
|
+
operation=f"update_{type_name}",
|
|
322
|
+
target_id=obj.id,
|
|
323
|
+
target_type=type_name,
|
|
324
|
+
source=_to_provenance_source(source_str),
|
|
325
|
+
merge_result=merge_result,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Collect warnings
|
|
329
|
+
warnings: list[str] = []
|
|
330
|
+
for record in merge_result.field_records:
|
|
331
|
+
if record.resolution.value == "warning":
|
|
332
|
+
warnings.append(record.reason)
|
|
333
|
+
elif record.resolution.value == "rejected":
|
|
334
|
+
warnings.append(
|
|
335
|
+
f"Field '{record.field_name}' rejected: {record.reason}"
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
return WriteResult(
|
|
339
|
+
success=True,
|
|
340
|
+
new_map=new_map,
|
|
341
|
+
new_version=bump.new_version,
|
|
342
|
+
merge_summary=log_entry,
|
|
343
|
+
errors=(),
|
|
344
|
+
warnings=tuple(warnings),
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def _execute_remove(
|
|
349
|
+
current_map: ComposMap,
|
|
350
|
+
obj_id: str,
|
|
351
|
+
obj_type: type,
|
|
352
|
+
reason: str,
|
|
353
|
+
caller_source: ProvenanceSource = ProvenanceSource.HUMAN_INPUT,
|
|
354
|
+
git_commit: str | None = None,
|
|
355
|
+
) -> WriteResult:
|
|
356
|
+
"""Pipeline for REMOVE operations.
|
|
357
|
+
|
|
358
|
+
Provenance-gated: caller_source must be >= the object's provenance source
|
|
359
|
+
in the trust hierarchy. If not, removal is rejected with a warning.
|
|
360
|
+
"""
|
|
361
|
+
type_name = _TYPE_TO_NAME[obj_type]
|
|
362
|
+
collection = _TYPE_TO_COLLECTION[obj_type]
|
|
363
|
+
|
|
364
|
+
# Find existing
|
|
365
|
+
existing = _find_existing(current_map, obj_id, obj_type)
|
|
366
|
+
if existing is None:
|
|
367
|
+
return _fail("not_found", f"{type_name} '{obj_id}' not found")
|
|
368
|
+
|
|
369
|
+
# Provenance gate: check caller has sufficient priority
|
|
370
|
+
source_str = _normalize_source(existing.provenance.source)
|
|
371
|
+
existing_source = _to_provenance_source(source_str)
|
|
372
|
+
existing_priority = _SOURCE_PRIORITY[existing_source]
|
|
373
|
+
caller_priority = _SOURCE_PRIORITY[caller_source]
|
|
374
|
+
|
|
375
|
+
if caller_priority < existing_priority:
|
|
376
|
+
return _fail(
|
|
377
|
+
"provenance",
|
|
378
|
+
(
|
|
379
|
+
f"Cannot remove {type_name} '{obj_id}': created by "
|
|
380
|
+
f"{existing_source.value}, caller is {caller_source.value}. "
|
|
381
|
+
f"Flagged for human review."
|
|
382
|
+
),
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Stage 4: Check remove blocked (impact report)
|
|
386
|
+
blocked = check_remove_blocked(current_map, obj_id, type_name)
|
|
387
|
+
if blocked:
|
|
388
|
+
blocking_desc = ", ".join(
|
|
389
|
+
f"{b.object_type}:{b.object_id}" for b in blocked
|
|
390
|
+
)
|
|
391
|
+
return _fail(
|
|
392
|
+
"integrity",
|
|
393
|
+
f"Cannot remove {type_name} '{obj_id}': referenced by {blocking_desc}",
|
|
394
|
+
blocked,
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
# Build soft-deleted object — type-aware status and fields
|
|
398
|
+
now = datetime.now(UTC)
|
|
399
|
+
existing_data = existing.model_dump(by_alias=False)
|
|
400
|
+
existing_data["status"] = _TYPE_TO_REMOVED_STATUS[obj_type]
|
|
401
|
+
existing_data["removed_at"] = now
|
|
402
|
+
existing_data["removed_reason"] = reason
|
|
403
|
+
removed_obj = type(existing).model_validate(existing_data)
|
|
404
|
+
|
|
405
|
+
# Build new map
|
|
406
|
+
new_map = _replace_in_collection(current_map, removed_obj, collection)
|
|
407
|
+
|
|
408
|
+
# Version bump
|
|
409
|
+
bump = increment_version(current_map, git_commit=git_commit)
|
|
410
|
+
new_map = _apply_version_bump(new_map, bump)
|
|
411
|
+
|
|
412
|
+
# Merge log entry
|
|
413
|
+
log_entry = create_entry(
|
|
414
|
+
version_bump=bump,
|
|
415
|
+
operation=f"remove_{type_name}",
|
|
416
|
+
target_id=obj_id,
|
|
417
|
+
target_type=type_name,
|
|
418
|
+
source=existing_source,
|
|
419
|
+
merge_result=None,
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
return WriteResult(
|
|
423
|
+
success=True,
|
|
424
|
+
new_map=new_map,
|
|
425
|
+
new_version=bump.new_version,
|
|
426
|
+
merge_summary=log_entry,
|
|
427
|
+
errors=(),
|
|
428
|
+
warnings=(),
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
# ---------------------------------------------------------------------------
|
|
433
|
+
# Type-specific public entry points
|
|
434
|
+
# ---------------------------------------------------------------------------
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def register_component(
|
|
438
|
+
current_map: ComposMap,
|
|
439
|
+
component: Component,
|
|
440
|
+
git_commit: str | None = None,
|
|
441
|
+
) -> WriteResult:
|
|
442
|
+
return _execute_register(current_map, component, git_commit)
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def register_relationship(
|
|
446
|
+
current_map: ComposMap,
|
|
447
|
+
relationship: Relationship,
|
|
448
|
+
git_commit: str | None = None,
|
|
449
|
+
) -> WriteResult:
|
|
450
|
+
return _execute_register(current_map, relationship, git_commit)
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def register_constraint(
|
|
454
|
+
current_map: ComposMap,
|
|
455
|
+
constraint: Constraint,
|
|
456
|
+
git_commit: str | None = None,
|
|
457
|
+
) -> WriteResult:
|
|
458
|
+
return _execute_register(current_map, constraint, git_commit)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def register_risk(
|
|
462
|
+
current_map: ComposMap,
|
|
463
|
+
risk: Risk,
|
|
464
|
+
git_commit: str | None = None,
|
|
465
|
+
) -> WriteResult:
|
|
466
|
+
return _execute_register(current_map, risk, git_commit)
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def register_decision(
|
|
470
|
+
current_map: ComposMap,
|
|
471
|
+
decision: Decision,
|
|
472
|
+
git_commit: str | None = None,
|
|
473
|
+
) -> WriteResult:
|
|
474
|
+
return _execute_register(current_map, decision, git_commit)
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def update_component(
|
|
478
|
+
current_map: ComposMap,
|
|
479
|
+
component: Component,
|
|
480
|
+
git_commit: str | None = None,
|
|
481
|
+
) -> WriteResult:
|
|
482
|
+
return _execute_update(current_map, component, git_commit)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def update_relationship(
|
|
486
|
+
current_map: ComposMap,
|
|
487
|
+
relationship: Relationship,
|
|
488
|
+
git_commit: str | None = None,
|
|
489
|
+
) -> WriteResult:
|
|
490
|
+
return _execute_update(current_map, relationship, git_commit)
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def update_constraint(
|
|
494
|
+
current_map: ComposMap,
|
|
495
|
+
constraint: Constraint,
|
|
496
|
+
git_commit: str | None = None,
|
|
497
|
+
) -> WriteResult:
|
|
498
|
+
return _execute_update(current_map, constraint, git_commit)
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def update_risk(
|
|
502
|
+
current_map: ComposMap,
|
|
503
|
+
risk: Risk,
|
|
504
|
+
git_commit: str | None = None,
|
|
505
|
+
) -> WriteResult:
|
|
506
|
+
return _execute_update(current_map, risk, git_commit)
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def update_decision(
|
|
510
|
+
current_map: ComposMap,
|
|
511
|
+
decision: Decision,
|
|
512
|
+
git_commit: str | None = None,
|
|
513
|
+
) -> WriteResult:
|
|
514
|
+
return _execute_update(current_map, decision, git_commit)
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def remove_component(
|
|
518
|
+
current_map: ComposMap,
|
|
519
|
+
component_id: str,
|
|
520
|
+
reason: str,
|
|
521
|
+
caller_source: ProvenanceSource = ProvenanceSource.HUMAN_INPUT,
|
|
522
|
+
git_commit: str | None = None,
|
|
523
|
+
) -> WriteResult:
|
|
524
|
+
return _execute_remove(
|
|
525
|
+
current_map, component_id, Component, reason, caller_source, git_commit,
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
def remove_relationship(
|
|
530
|
+
current_map: ComposMap,
|
|
531
|
+
relationship_id: str,
|
|
532
|
+
reason: str,
|
|
533
|
+
caller_source: ProvenanceSource = ProvenanceSource.HUMAN_INPUT,
|
|
534
|
+
git_commit: str | None = None,
|
|
535
|
+
) -> WriteResult:
|
|
536
|
+
return _execute_remove(
|
|
537
|
+
current_map, relationship_id, Relationship, reason, caller_source, git_commit,
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def remove_constraint(
|
|
542
|
+
current_map: ComposMap,
|
|
543
|
+
constraint_id: str,
|
|
544
|
+
reason: str,
|
|
545
|
+
caller_source: ProvenanceSource = ProvenanceSource.HUMAN_INPUT,
|
|
546
|
+
git_commit: str | None = None,
|
|
547
|
+
) -> WriteResult:
|
|
548
|
+
return _execute_remove(
|
|
549
|
+
current_map, constraint_id, Constraint, reason, caller_source, git_commit,
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
def remove_risk(
|
|
554
|
+
current_map: ComposMap,
|
|
555
|
+
risk_id: str,
|
|
556
|
+
reason: str,
|
|
557
|
+
caller_source: ProvenanceSource = ProvenanceSource.HUMAN_INPUT,
|
|
558
|
+
git_commit: str | None = None,
|
|
559
|
+
) -> WriteResult:
|
|
560
|
+
return _execute_remove(
|
|
561
|
+
current_map, risk_id, Risk, reason, caller_source, git_commit,
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def remove_decision(
|
|
566
|
+
current_map: ComposMap,
|
|
567
|
+
decision_id: str,
|
|
568
|
+
reason: str,
|
|
569
|
+
caller_source: ProvenanceSource = ProvenanceSource.HUMAN_INPUT,
|
|
570
|
+
git_commit: str | None = None,
|
|
571
|
+
) -> WriteResult:
|
|
572
|
+
return _execute_remove(
|
|
573
|
+
current_map, decision_id, Decision, reason, caller_source, git_commit,
|
|
574
|
+
)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Schema package — Pydantic models for the Compos architectural map."""
|
|
2
|
+
|
|
3
|
+
from compos.schema.models import (
|
|
4
|
+
AlternativeConsidered,
|
|
5
|
+
Component,
|
|
6
|
+
ComponentType,
|
|
7
|
+
ComposMap,
|
|
8
|
+
Constraint,
|
|
9
|
+
ConstraintStatus,
|
|
10
|
+
ConstraintType,
|
|
11
|
+
Decision,
|
|
12
|
+
DecisionProvenance,
|
|
13
|
+
DecisionProvenanceSource,
|
|
14
|
+
DecisionStatus,
|
|
15
|
+
Hardness,
|
|
16
|
+
LastMerge,
|
|
17
|
+
Likelihood,
|
|
18
|
+
ObjectStatus,
|
|
19
|
+
ProjectInfo,
|
|
20
|
+
Provenance,
|
|
21
|
+
ProvenanceSource,
|
|
22
|
+
Relationship,
|
|
23
|
+
RelationshipPattern,
|
|
24
|
+
RelationshipType,
|
|
25
|
+
Risk,
|
|
26
|
+
RiskStatus,
|
|
27
|
+
Severity,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
# Enums
|
|
32
|
+
"ComponentType",
|
|
33
|
+
"ConstraintStatus",
|
|
34
|
+
"ConstraintType",
|
|
35
|
+
"DecisionProvenanceSource",
|
|
36
|
+
"DecisionStatus",
|
|
37
|
+
"Hardness",
|
|
38
|
+
"Likelihood",
|
|
39
|
+
"ObjectStatus",
|
|
40
|
+
"ProvenanceSource",
|
|
41
|
+
"RelationshipPattern",
|
|
42
|
+
"RelationshipType",
|
|
43
|
+
"RiskStatus",
|
|
44
|
+
"Severity",
|
|
45
|
+
# Models
|
|
46
|
+
"AlternativeConsidered",
|
|
47
|
+
"Component",
|
|
48
|
+
"ComposMap",
|
|
49
|
+
"Constraint",
|
|
50
|
+
"Decision",
|
|
51
|
+
"DecisionProvenance",
|
|
52
|
+
"LastMerge",
|
|
53
|
+
"ProjectInfo",
|
|
54
|
+
"Provenance",
|
|
55
|
+
"Relationship",
|
|
56
|
+
"Risk",
|
|
57
|
+
]
|