elspais 0.11.2__py3-none-any.whl → 0.43.5__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.
- elspais/__init__.py +1 -10
- elspais/{sponsors/__init__.py → associates.py} +102 -56
- elspais/cli.py +366 -69
- elspais/commands/__init__.py +9 -3
- elspais/commands/analyze.py +118 -169
- elspais/commands/changed.py +12 -23
- elspais/commands/config_cmd.py +10 -13
- elspais/commands/edit.py +33 -13
- elspais/commands/example_cmd.py +319 -0
- elspais/commands/hash_cmd.py +161 -183
- elspais/commands/health.py +1177 -0
- elspais/commands/index.py +98 -115
- elspais/commands/init.py +99 -22
- elspais/commands/reformat_cmd.py +41 -433
- elspais/commands/rules_cmd.py +2 -2
- elspais/commands/trace.py +443 -324
- elspais/commands/validate.py +193 -411
- elspais/config/__init__.py +799 -5
- elspais/{core/content_rules.py → content_rules.py} +20 -2
- elspais/docs/cli/assertions.md +67 -0
- elspais/docs/cli/commands.md +304 -0
- elspais/docs/cli/config.md +262 -0
- elspais/docs/cli/format.md +66 -0
- elspais/docs/cli/git.md +45 -0
- elspais/docs/cli/health.md +190 -0
- elspais/docs/cli/hierarchy.md +60 -0
- elspais/docs/cli/ignore.md +72 -0
- elspais/docs/cli/mcp.md +245 -0
- elspais/docs/cli/quickstart.md +58 -0
- elspais/docs/cli/traceability.md +89 -0
- elspais/docs/cli/validation.md +96 -0
- elspais/graph/GraphNode.py +383 -0
- elspais/graph/__init__.py +40 -0
- elspais/graph/annotators.py +927 -0
- elspais/graph/builder.py +1886 -0
- elspais/graph/deserializer.py +248 -0
- elspais/graph/factory.py +284 -0
- elspais/graph/metrics.py +127 -0
- elspais/graph/mutations.py +161 -0
- elspais/graph/parsers/__init__.py +156 -0
- elspais/graph/parsers/code.py +213 -0
- elspais/graph/parsers/comments.py +112 -0
- elspais/graph/parsers/config_helpers.py +29 -0
- elspais/graph/parsers/heredocs.py +225 -0
- elspais/graph/parsers/journey.py +131 -0
- elspais/graph/parsers/remainder.py +79 -0
- elspais/graph/parsers/requirement.py +347 -0
- elspais/graph/parsers/results/__init__.py +6 -0
- elspais/graph/parsers/results/junit_xml.py +229 -0
- elspais/graph/parsers/results/pytest_json.py +313 -0
- elspais/graph/parsers/test.py +305 -0
- elspais/graph/relations.py +78 -0
- elspais/graph/serialize.py +216 -0
- elspais/html/__init__.py +8 -0
- elspais/html/generator.py +731 -0
- elspais/html/templates/trace_view.html.j2 +2151 -0
- elspais/mcp/__init__.py +45 -29
- elspais/mcp/__main__.py +5 -1
- elspais/mcp/file_mutations.py +138 -0
- elspais/mcp/server.py +1998 -244
- elspais/testing/__init__.py +3 -3
- elspais/testing/config.py +3 -0
- elspais/testing/mapper.py +1 -1
- elspais/testing/scanner.py +301 -12
- elspais/utilities/__init__.py +1 -0
- elspais/utilities/docs_loader.py +115 -0
- elspais/utilities/git.py +607 -0
- elspais/{core → utilities}/hasher.py +8 -22
- elspais/utilities/md_renderer.py +189 -0
- elspais/{core → utilities}/patterns.py +56 -51
- elspais/utilities/reference_config.py +626 -0
- elspais/validation/__init__.py +19 -0
- elspais/validation/format.py +264 -0
- {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/METADATA +7 -4
- elspais-0.43.5.dist-info/RECORD +80 -0
- elspais/config/defaults.py +0 -179
- elspais/config/loader.py +0 -494
- elspais/core/__init__.py +0 -21
- elspais/core/git.py +0 -346
- elspais/core/models.py +0 -320
- elspais/core/parser.py +0 -639
- elspais/core/rules.py +0 -509
- elspais/mcp/context.py +0 -172
- elspais/mcp/serializers.py +0 -112
- elspais/reformat/__init__.py +0 -50
- elspais/reformat/detector.py +0 -112
- elspais/reformat/hierarchy.py +0 -247
- elspais/reformat/line_breaks.py +0 -218
- elspais/reformat/prompts.py +0 -133
- elspais/reformat/transformer.py +0 -266
- elspais/trace_view/__init__.py +0 -55
- elspais/trace_view/coverage.py +0 -183
- elspais/trace_view/generators/__init__.py +0 -12
- elspais/trace_view/generators/base.py +0 -334
- elspais/trace_view/generators/csv.py +0 -118
- elspais/trace_view/generators/markdown.py +0 -170
- elspais/trace_view/html/__init__.py +0 -33
- elspais/trace_view/html/generator.py +0 -1140
- elspais/trace_view/html/templates/base.html +0 -283
- elspais/trace_view/html/templates/components/code_viewer_modal.html +0 -14
- elspais/trace_view/html/templates/components/file_picker_modal.html +0 -20
- elspais/trace_view/html/templates/components/legend_modal.html +0 -69
- elspais/trace_view/html/templates/components/review_panel.html +0 -118
- elspais/trace_view/html/templates/partials/review/help/help-panel.json +0 -244
- elspais/trace_view/html/templates/partials/review/help/onboarding.json +0 -77
- elspais/trace_view/html/templates/partials/review/help/tooltips.json +0 -237
- elspais/trace_view/html/templates/partials/review/review-comments.js +0 -928
- elspais/trace_view/html/templates/partials/review/review-data.js +0 -961
- elspais/trace_view/html/templates/partials/review/review-help.js +0 -679
- elspais/trace_view/html/templates/partials/review/review-init.js +0 -177
- elspais/trace_view/html/templates/partials/review/review-line-numbers.js +0 -429
- elspais/trace_view/html/templates/partials/review/review-packages.js +0 -1029
- elspais/trace_view/html/templates/partials/review/review-position.js +0 -540
- elspais/trace_view/html/templates/partials/review/review-resize.js +0 -115
- elspais/trace_view/html/templates/partials/review/review-status.js +0 -659
- elspais/trace_view/html/templates/partials/review/review-sync.js +0 -992
- elspais/trace_view/html/templates/partials/review-styles.css +0 -2238
- elspais/trace_view/html/templates/partials/scripts.js +0 -1741
- elspais/trace_view/html/templates/partials/styles.css +0 -1756
- elspais/trace_view/models.py +0 -378
- elspais/trace_view/review/__init__.py +0 -63
- elspais/trace_view/review/branches.py +0 -1142
- elspais/trace_view/review/models.py +0 -1200
- elspais/trace_view/review/position.py +0 -591
- elspais/trace_view/review/server.py +0 -1032
- elspais/trace_view/review/status.py +0 -455
- elspais/trace_view/review/storage.py +0 -1343
- elspais/trace_view/scanning.py +0 -213
- elspais/trace_view/specs/README.md +0 -84
- elspais/trace_view/specs/tv-d00001-template-architecture.md +0 -36
- elspais/trace_view/specs/tv-d00002-css-extraction.md +0 -37
- elspais/trace_view/specs/tv-d00003-js-extraction.md +0 -43
- elspais/trace_view/specs/tv-d00004-build-embedding.md +0 -40
- elspais/trace_view/specs/tv-d00005-test-format.md +0 -78
- elspais/trace_view/specs/tv-d00010-review-data-models.md +0 -33
- elspais/trace_view/specs/tv-d00011-review-storage.md +0 -33
- elspais/trace_view/specs/tv-d00012-position-resolution.md +0 -33
- elspais/trace_view/specs/tv-d00013-git-branches.md +0 -31
- elspais/trace_view/specs/tv-d00014-review-api-server.md +0 -31
- elspais/trace_view/specs/tv-d00015-status-modifier.md +0 -27
- elspais/trace_view/specs/tv-d00016-js-integration.md +0 -33
- elspais/trace_view/specs/tv-p00001-html-generator.md +0 -33
- elspais/trace_view/specs/tv-p00002-review-system.md +0 -29
- elspais-0.11.2.dist-info/RECORD +0 -101
- {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/WHEEL +0 -0
- {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/entry_points.txt +0 -0
- {elspais-0.11.2.dist-info → elspais-0.43.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""Format validation - Validate requirement format against configurable rules.
|
|
2
|
+
|
|
3
|
+
Validates requirements against rules defined in [rules.format] config section:
|
|
4
|
+
- require_hash: Check hash exists in footer
|
|
5
|
+
- require_assertions: Check at least one assertion exists
|
|
6
|
+
- require_rationale: Check rationale section exists
|
|
7
|
+
- require_shall: Check "shall" keyword in assertion text
|
|
8
|
+
- require_status: Check status field in metadata
|
|
9
|
+
- allowed_statuses: List of valid status values
|
|
10
|
+
- labels_sequential: Check assertion labels are sequential (A,B,C not A,C,E)
|
|
11
|
+
- labels_unique: Check no duplicate assertion labels
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from typing import TYPE_CHECKING, Any
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from elspais.graph import GraphNode
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class FormatRulesConfig:
|
|
25
|
+
"""Configuration for format validation rules.
|
|
26
|
+
|
|
27
|
+
All rules default to False (disabled) for backwards compatibility.
|
|
28
|
+
Enable rules explicitly in [rules.format] config section.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
require_hash: bool = False
|
|
32
|
+
require_assertions: bool = False
|
|
33
|
+
require_rationale: bool = False
|
|
34
|
+
require_shall: bool = False
|
|
35
|
+
require_status: bool = False
|
|
36
|
+
allowed_statuses: list[str] = field(default_factory=list)
|
|
37
|
+
labels_sequential: bool = False
|
|
38
|
+
labels_unique: bool = True # Default True - duplicates are usually errors
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def from_dict(cls, data: dict[str, Any]) -> FormatRulesConfig:
|
|
42
|
+
"""Create FormatRulesConfig from configuration dictionary.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
data: Dictionary from [rules.format] config section
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
FormatRulesConfig instance
|
|
49
|
+
"""
|
|
50
|
+
return cls(
|
|
51
|
+
require_hash=data.get("require_hash", False),
|
|
52
|
+
require_assertions=data.get("require_assertions", False),
|
|
53
|
+
require_rationale=data.get("require_rationale", False),
|
|
54
|
+
require_shall=data.get("require_shall", False),
|
|
55
|
+
require_status=data.get("require_status", False),
|
|
56
|
+
allowed_statuses=data.get("allowed_statuses", []),
|
|
57
|
+
labels_sequential=data.get("labels_sequential", False),
|
|
58
|
+
labels_unique=data.get("labels_unique", True),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class FormatViolation:
|
|
64
|
+
"""A format rule violation found during validation.
|
|
65
|
+
|
|
66
|
+
Attributes:
|
|
67
|
+
rule: The rule that was violated
|
|
68
|
+
message: Human-readable description of the violation
|
|
69
|
+
severity: "error" or "warning"
|
|
70
|
+
node_id: ID of the requirement node with the violation
|
|
71
|
+
details: Additional context about the violation
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
rule: str
|
|
75
|
+
message: str
|
|
76
|
+
severity: str = "error"
|
|
77
|
+
node_id: str = ""
|
|
78
|
+
details: dict[str, Any] = field(default_factory=dict)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def validate_requirement_format(
|
|
82
|
+
node: GraphNode,
|
|
83
|
+
rules: FormatRulesConfig,
|
|
84
|
+
) -> list[FormatViolation]:
|
|
85
|
+
"""Validate a requirement node against format rules.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
node: The requirement GraphNode to validate
|
|
89
|
+
rules: Format rules configuration
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
List of FormatViolation objects (empty if valid)
|
|
93
|
+
"""
|
|
94
|
+
from elspais.graph import NodeKind
|
|
95
|
+
|
|
96
|
+
violations: list[FormatViolation] = []
|
|
97
|
+
|
|
98
|
+
# Only validate requirement nodes
|
|
99
|
+
if node.kind != NodeKind.REQUIREMENT:
|
|
100
|
+
return violations
|
|
101
|
+
|
|
102
|
+
node_id = node.id
|
|
103
|
+
|
|
104
|
+
# Rule: require_hash
|
|
105
|
+
if rules.require_hash:
|
|
106
|
+
hash_value = node.hash # Use convenience property
|
|
107
|
+
if not hash_value or hash_value == "00000000":
|
|
108
|
+
violations.append(
|
|
109
|
+
FormatViolation(
|
|
110
|
+
rule="require_hash",
|
|
111
|
+
message=f"{node_id}: Missing or placeholder hash value",
|
|
112
|
+
node_id=node_id,
|
|
113
|
+
details={"current_hash": hash_value},
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Rule: require_assertions
|
|
118
|
+
if rules.require_assertions:
|
|
119
|
+
# Count assertion children
|
|
120
|
+
assertion_count = sum(
|
|
121
|
+
1 for child in node.iter_children() if child.kind == NodeKind.ASSERTION
|
|
122
|
+
)
|
|
123
|
+
if assertion_count == 0:
|
|
124
|
+
violations.append(
|
|
125
|
+
FormatViolation(
|
|
126
|
+
rule="require_assertions",
|
|
127
|
+
message=f"{node_id}: No assertions defined",
|
|
128
|
+
node_id=node_id,
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Rule: require_rationale
|
|
133
|
+
if rules.require_rationale:
|
|
134
|
+
rationale = node.get_field("rationale", "")
|
|
135
|
+
if not rationale or not rationale.strip():
|
|
136
|
+
violations.append(
|
|
137
|
+
FormatViolation(
|
|
138
|
+
rule="require_rationale",
|
|
139
|
+
message=f"{node_id}: Missing rationale section",
|
|
140
|
+
node_id=node_id,
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Rule: require_shall
|
|
145
|
+
if rules.require_shall:
|
|
146
|
+
# Check assertions for SHALL keyword
|
|
147
|
+
assertions_without_shall = []
|
|
148
|
+
for child in node.iter_children():
|
|
149
|
+
if child.kind == NodeKind.ASSERTION:
|
|
150
|
+
assertion_text = child.get_field("text", "") or child.get_label()
|
|
151
|
+
if "shall" not in assertion_text.lower():
|
|
152
|
+
assertions_without_shall.append(child.get_label())
|
|
153
|
+
|
|
154
|
+
if assertions_without_shall:
|
|
155
|
+
violations.append(
|
|
156
|
+
FormatViolation(
|
|
157
|
+
rule="require_shall",
|
|
158
|
+
message=(
|
|
159
|
+
f"{node_id}: Assertions missing 'SHALL' keyword: "
|
|
160
|
+
f"{', '.join(assertions_without_shall)}"
|
|
161
|
+
),
|
|
162
|
+
node_id=node_id,
|
|
163
|
+
details={"assertions": assertions_without_shall},
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Rule: require_status
|
|
168
|
+
if rules.require_status:
|
|
169
|
+
status = node.status # Use convenience property
|
|
170
|
+
if not status:
|
|
171
|
+
violations.append(
|
|
172
|
+
FormatViolation(
|
|
173
|
+
rule="require_status",
|
|
174
|
+
message=f"{node_id}: Missing status field",
|
|
175
|
+
node_id=node_id,
|
|
176
|
+
)
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Rule: allowed_statuses
|
|
180
|
+
if rules.allowed_statuses:
|
|
181
|
+
status = node.status # Use convenience property
|
|
182
|
+
if status and status not in rules.allowed_statuses:
|
|
183
|
+
violations.append(
|
|
184
|
+
FormatViolation(
|
|
185
|
+
rule="allowed_statuses",
|
|
186
|
+
message=(
|
|
187
|
+
f"{node_id}: Invalid status '{status}' "
|
|
188
|
+
f"(allowed: {', '.join(rules.allowed_statuses)})"
|
|
189
|
+
),
|
|
190
|
+
node_id=node_id,
|
|
191
|
+
details={"current": status, "allowed": rules.allowed_statuses},
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Collect assertion labels for sequential and unique checks
|
|
196
|
+
assertion_labels: list[str] = []
|
|
197
|
+
for child in node.iter_children():
|
|
198
|
+
if child.kind == NodeKind.ASSERTION:
|
|
199
|
+
# Extract label from assertion ID (e.g., "REQ-p00001-A" -> "A")
|
|
200
|
+
label = child.get_field("label")
|
|
201
|
+
if not label and "-" in child.id:
|
|
202
|
+
label = child.id.split("-")[-1]
|
|
203
|
+
if label:
|
|
204
|
+
assertion_labels.append(label)
|
|
205
|
+
|
|
206
|
+
# Rule: labels_unique
|
|
207
|
+
if rules.labels_unique and assertion_labels:
|
|
208
|
+
seen = set()
|
|
209
|
+
duplicates = []
|
|
210
|
+
for label in assertion_labels:
|
|
211
|
+
if label in seen:
|
|
212
|
+
duplicates.append(label)
|
|
213
|
+
seen.add(label)
|
|
214
|
+
|
|
215
|
+
if duplicates:
|
|
216
|
+
violations.append(
|
|
217
|
+
FormatViolation(
|
|
218
|
+
rule="labels_unique",
|
|
219
|
+
message=f"{node_id}: Duplicate assertion labels: {', '.join(set(duplicates))}",
|
|
220
|
+
node_id=node_id,
|
|
221
|
+
details={"duplicates": list(set(duplicates))},
|
|
222
|
+
)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Rule: labels_sequential
|
|
226
|
+
if rules.labels_sequential and assertion_labels:
|
|
227
|
+
# Check if uppercase letter labels are sequential
|
|
228
|
+
uppercase_labels = [lbl for lbl in assertion_labels if len(lbl) == 1 and lbl.isupper()]
|
|
229
|
+
if uppercase_labels:
|
|
230
|
+
sorted_labels = sorted(set(uppercase_labels))
|
|
231
|
+
expected_next = "A"
|
|
232
|
+
gaps = []
|
|
233
|
+
|
|
234
|
+
for label in sorted_labels:
|
|
235
|
+
if label != expected_next:
|
|
236
|
+
gaps.append(f"expected {expected_next}, found {label}")
|
|
237
|
+
break
|
|
238
|
+
expected_next = chr(ord(expected_next) + 1)
|
|
239
|
+
|
|
240
|
+
if gaps:
|
|
241
|
+
violations.append(
|
|
242
|
+
FormatViolation(
|
|
243
|
+
rule="labels_sequential",
|
|
244
|
+
message=f"{node_id}: Non-sequential assertion labels ({gaps[0]})",
|
|
245
|
+
node_id=node_id,
|
|
246
|
+
severity="warning", # Less severe - sometimes intentional
|
|
247
|
+
details={"labels": uppercase_labels, "gaps": gaps},
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
return violations
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def get_format_rules_config(config: dict[str, Any]) -> FormatRulesConfig:
|
|
255
|
+
"""Get FormatRulesConfig from configuration dictionary.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
config: Full configuration dictionary from get_config()
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
FormatRulesConfig instance from [rules.format] section
|
|
262
|
+
"""
|
|
263
|
+
rules_data = config.get("rules", {}).get("format", {})
|
|
264
|
+
return FormatRulesConfig.from_dict(rules_data)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: elspais
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.43.5
|
|
4
4
|
Summary: Requirements validation and traceability tools - L-Space connects all libraries
|
|
5
5
|
Project-URL: Homepage, https://github.com/anspar/elspais
|
|
6
6
|
Project-URL: Documentation, https://github.com/anspar/elspais#readme
|
|
@@ -25,12 +25,15 @@ Classifier: Topic :: Software Development :: Quality Assurance
|
|
|
25
25
|
Classifier: Typing :: Typed
|
|
26
26
|
Requires-Python: >=3.9
|
|
27
27
|
Provides-Extra: all
|
|
28
|
+
Requires-Dist: argcomplete>=3.0; extra == 'all'
|
|
28
29
|
Requires-Dist: flask-cors>=4.0; extra == 'all'
|
|
29
30
|
Requires-Dist: flask>=2.0; extra == 'all'
|
|
30
31
|
Requires-Dist: jinja2>=3.0; extra == 'all'
|
|
31
32
|
Requires-Dist: mcp>=1.0; extra == 'all'
|
|
32
33
|
Provides-Extra: binary
|
|
33
34
|
Requires-Dist: pyinstaller>=6.0; extra == 'binary'
|
|
35
|
+
Provides-Extra: completion
|
|
36
|
+
Requires-Dist: argcomplete>=3.0; extra == 'completion'
|
|
34
37
|
Provides-Extra: dev
|
|
35
38
|
Requires-Dist: black>=23.0; extra == 'dev'
|
|
36
39
|
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
@@ -97,7 +100,7 @@ FROM python:3.11-slim
|
|
|
97
100
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
|
98
101
|
|
|
99
102
|
# Install elspais (10-100x faster than pip)
|
|
100
|
-
RUN uv pip install --system --no-cache elspais==0.
|
|
103
|
+
RUN uv pip install --system --no-cache elspais==0.24.3
|
|
101
104
|
```
|
|
102
105
|
|
|
103
106
|
```yaml
|
|
@@ -106,7 +109,7 @@ RUN uv pip install --system --no-cache elspais==0.9.3
|
|
|
106
109
|
uses: astral-sh/setup-uv@v2
|
|
107
110
|
|
|
108
111
|
- name: Install elspais
|
|
109
|
-
run: uv pip install --system elspais==0.
|
|
112
|
+
run: uv pip install --system elspais==0.24.3
|
|
110
113
|
```
|
|
111
114
|
|
|
112
115
|
**Note:** For regulated/medical software projects, always pin the exact version for reproducibility.
|
|
@@ -424,7 +427,7 @@ For reproducible builds, pin the version in your project:
|
|
|
424
427
|
|
|
425
428
|
```bash
|
|
426
429
|
# .github/versions.env
|
|
427
|
-
ELSPAIS_VERSION=0.
|
|
430
|
+
ELSPAIS_VERSION=0.24.3
|
|
428
431
|
```
|
|
429
432
|
|
|
430
433
|
```yaml
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
elspais/__init__.py,sha256=ddJTTPg5aTpGZSeriPjIiSW5fxnYWQ33Lquah3HLw6g,730
|
|
2
|
+
elspais/__main__.py,sha256=rCMaObqJeT_6dhyfND7S4dh_lv30j7Ww3Z7992YYwaE,130
|
|
3
|
+
elspais/associates.py,sha256=Wtue2cn60VkLGoIMQDAZeOJtuOdfJjVbXsIc7EKuun0,14221
|
|
4
|
+
elspais/cli.py,sha256=ccIvB7JseseSXDCLXv8euyIfK4BrEV-ttTSFj3VgLd0,29369
|
|
5
|
+
elspais/content_rules.py,sha256=YEjsMB07gjcWymlRRf2z1E39-RvZMHXAwVxypo7ylog,4829
|
|
6
|
+
elspais/commands/__init__.py,sha256=E8otW6goXASgd0cDvG3SiuqXxC-U6rfyRCnN8ZI3tcQ,256
|
|
7
|
+
elspais/commands/analyze.py,sha256=t4THSyUytRjoECMToDSELj6OKJu_BTiEuCQsmRt18EY,5197
|
|
8
|
+
elspais/commands/changed.py,sha256=DmZ8taBHOKrckiqm5RNA0zUJKMYAoWNCPzVZSUD2ugM,4524
|
|
9
|
+
elspais/commands/config_cmd.py,sha256=r1BATGOVYOuFNd1gl2AnktMj4LOSc1O7KuzykPExTpo,13995
|
|
10
|
+
elspais/commands/edit.py,sha256=_ok7MWUhtYviCFSf6ZR_ol6Ab0HPn4NTMIuZFTEBejc,16784
|
|
11
|
+
elspais/commands/example_cmd.py,sha256=DGByHbTwj0iyeabtJCD7o3Vx5crj9IpgsZ01ZnmmI98,8804
|
|
12
|
+
elspais/commands/hash_cmd.py,sha256=dNvH0dzBpyshvpe6Dz9iFODLb-k9UA_qPyVQfyz4h2E,6010
|
|
13
|
+
elspais/commands/health.py,sha256=ihE_pPPFySI3opNPRTShhY9Mr4PH7g8qb7W297oS7vo,37801
|
|
14
|
+
elspais/commands/index.py,sha256=JbNyGjaS3GqElIkAV1_fKyLuIcna1Kko6fPaetZ3DuY,4569
|
|
15
|
+
elspais/commands/init.py,sha256=fvGjuN6rDe_7bZvwwEW3Lqr7r4xgYXcsn1wlDOWVvPA,6939
|
|
16
|
+
elspais/commands/reformat_cmd.py,sha256=AFFGWX-OVx__xdMVvdbahLYK8G7d5Kdsm7oSHOAVX5E,1950
|
|
17
|
+
elspais/commands/rules_cmd.py,sha256=UwFzDoAXKzSpzjJ1LIJK1bfAkosnGwtS9k_CsPhUrH4,3451
|
|
18
|
+
elspais/commands/trace.py,sha256=iq-gF_M_OIC7_2kxYC3zT-V8RYJqOX-nmRsuujUMtbY,16432
|
|
19
|
+
elspais/commands/validate.py,sha256=iTrN9v6ZQM-HoNacVOzS1B1sp5JUi4FlOPtINZCCa9I,7456
|
|
20
|
+
elspais/config/__init__.py,sha256=LlfrMXtUd_E0bQIDBgSh29EpYAUqXMuGrPyp1qGUwE8,23272
|
|
21
|
+
elspais/graph/GraphNode.py,sha256=UuqcXPLzwdC2JyZhDaIHc2rGeZ9fACmPdsdzMGKjkmw,12327
|
|
22
|
+
elspais/graph/__init__.py,sha256=rifZItDPBLJRqsn1lDKLtfxzSF9kbJ-3Vl0byxlniRs,1196
|
|
23
|
+
elspais/graph/annotators.py,sha256=WGhR6gXXzQqYl8SxliECRpluUDnIwIM2kwt30JqyPfI,28464
|
|
24
|
+
elspais/graph/builder.py,sha256=Ro1VYBnN4sVfRUsPnUo2qA4ov9BxrNUkhLnk3H_yRnQ,70544
|
|
25
|
+
elspais/graph/deserializer.py,sha256=4jiGe3agIYDwrTNCShS-jKzdKK0ndZIvU__rX9c6xkU,7906
|
|
26
|
+
elspais/graph/factory.py,sha256=1VirAq1YO0nqPpa-Z8EvnlC1F3AuM_hZ-NE2uTGAMgI,11812
|
|
27
|
+
elspais/graph/metrics.py,sha256=Z5-rfm5DeEcwmdx4LltrWxCM104O2Cmw8uFZU8zCb4I,4811
|
|
28
|
+
elspais/graph/mutations.py,sha256=Nd0_S2CEMllYHYjwDdmKkMvnBvah7pSSquMniq47F5E,4882
|
|
29
|
+
elspais/graph/relations.py,sha256=O8Wbht2J3OuHx2Ah18FWUXhRA47Kc2xgeutQ7VAjnlU,2538
|
|
30
|
+
elspais/graph/serialize.py,sha256=U_WQKCbAAygg59Ocvjr8_f4--UdGtK534Ys4WDR2fOE,5571
|
|
31
|
+
elspais/graph/parsers/__init__.py,sha256=jqD4N5Z3qRzmCcz8jpSpo-NdBCvPZrcgSOAFt5ZH0gQ,4538
|
|
32
|
+
elspais/graph/parsers/code.py,sha256=9txDvj6umxHkP_t07HfNayMre1pNhUP1cScBEeBj_Qk,7422
|
|
33
|
+
elspais/graph/parsers/comments.py,sha256=JxB79XSg0wDhEkOgGhivk6evrft7IkcCBhdur82ZC4M,3469
|
|
34
|
+
elspais/graph/parsers/config_helpers.py,sha256=Vz3xitKnNgZbCSYKnBSeDCW1TKVyHlB9UbEzq-q21zg,995
|
|
35
|
+
elspais/graph/parsers/heredocs.py,sha256=kEyIZrGXOUWu-JJkfM56OD1uDYYBYOtzY1qoslhkGZQ,7219
|
|
36
|
+
elspais/graph/parsers/journey.py,sha256=Amj40qEq_zE7maz8ewR-djlSy5SiWQB6JeEIa_Q5Fic,3928
|
|
37
|
+
elspais/graph/parsers/remainder.py,sha256=u004gg9twwN_oW9hfqyDt_6MSEvxRrFNIeoAMVic7j4,2308
|
|
38
|
+
elspais/graph/parsers/requirement.py,sha256=HcSL5gFUpOmWHBoI1EOazvOszkVuEnSPIKhWR-k8DzI,12002
|
|
39
|
+
elspais/graph/parsers/test.py,sha256=EfM1eySf2bUYrPknfPGW1hOu_98_HA2V1K5ral5UQSg,11244
|
|
40
|
+
elspais/graph/parsers/results/__init__.py,sha256=QRo0_DIh4xar2kv4HKnqV2IRg7Rjbku9AGO8vJtolsA,254
|
|
41
|
+
elspais/graph/parsers/results/junit_xml.py,sha256=k_wRG_sblQMBdlVmoaMxDcHHqW30XQqHKhEhAY-UmNg,7821
|
|
42
|
+
elspais/graph/parsers/results/pytest_json.py,sha256=eZf3tXCm4Tv8oDdLsrCnbPShSaIrG55CoIegGxQs-nw,10395
|
|
43
|
+
elspais/html/__init__.py,sha256=csyyiBoU_WKkFyP5c4zTnx1uHwQ4sZlbA66OvFgpAis,212
|
|
44
|
+
elspais/html/generator.py,sha256=RJMEwzgrNLzmIPEomkGfJRogcyqQQj24sjpRJsdYOlg,27927
|
|
45
|
+
elspais/html/templates/trace_view.html.j2,sha256=6tUx9BgiQqjnThUZ4NG6sSKXH1F1pXGhaSM6n3CRDJc,76871
|
|
46
|
+
elspais/mcp/__init__.py,sha256=Sr-YJtQgYO2gKDD4vHwJ9si3PeAnoqX6DntUXM-qekI,1450
|
|
47
|
+
elspais/mcp/__main__.py,sha256=QgFm_G-BkqjW0cULzUfdLAeGeam5R4qneBCs5k2pUTc,182
|
|
48
|
+
elspais/mcp/file_mutations.py,sha256=ts2-m7-mupe96rkkQe6nS_u_K3JPf_rH02pANw8RhCA,4457
|
|
49
|
+
elspais/mcp/server.py,sha256=OEmUzpMLf_gront2E5MmFNnhSfoPaR_VFU_lXvNu2uI,77388
|
|
50
|
+
elspais/testing/__init__.py,sha256=RIGednU6uRUcYHNT5nbLTrDPY-SCh3HCYj0A-iEWCHg,916
|
|
51
|
+
elspais/testing/config.py,sha256=iobz9r6CHEcfx4r2QTWlGN6S8XWj3OJ0DEW7GyxmpLw,1813
|
|
52
|
+
elspais/testing/mapper.py,sha256=itcatI7vaVn7RkW6SSTb4bj0R8hZXINWVN8KtBf24iI,5657
|
|
53
|
+
elspais/testing/result_parser.py,sha256=YgSNQa3mlT-ovo3PzjoUypN2urR1dIunPcfdB2IMAH8,9415
|
|
54
|
+
elspais/testing/scanner.py,sha256=9YttAeHA1uNuyMMHaVVhotbAqVQQN5U2J5HVK7c6gC8,17724
|
|
55
|
+
elspais/utilities/__init__.py,sha256=_HN7tawOZdaopnl3ISEWqDp2L_3fzkWsx4iKu0kYohs,55
|
|
56
|
+
elspais/utilities/docs_loader.py,sha256=NiXOU-NAP97JMDR8w_H0xq6eMP7Gbq4NeDeoNgq-6h4,2693
|
|
57
|
+
elspais/utilities/git.py,sha256=yEw-ARaVjQIC8Fu5evAgmvi77mDLFesXdX1bd4v8yKU,19887
|
|
58
|
+
elspais/utilities/hasher.py,sha256=2gwHgdMYf6r87Rd1R2YEfIfz3KqymESu_DD1SgYFkXk,3782
|
|
59
|
+
elspais/utilities/md_renderer.py,sha256=nKKO4RxechfayduCC-jr2YmyzxPTfd5JY8W5kvsLOXs,6378
|
|
60
|
+
elspais/utilities/patterns.py,sha256=jCILSUsk9rBI18sVp9StBxw7lImnI1kBtEU7Bajlm1s,13050
|
|
61
|
+
elspais/utilities/reference_config.py,sha256=i0bA0NMplip2TY2XQvRPN1TvDoqafnuPv9PtsPMABdw,21461
|
|
62
|
+
elspais/validation/__init__.py,sha256=pgP-4_gkmkaBX1uRGiwzZf0n3kSJpYKCAQppIq65fpQ,441
|
|
63
|
+
elspais/validation/format.py,sha256=nlYF5WVPbygo2jlscDg4Bk3R4J7fyYVf362nfGvvPsY,9219
|
|
64
|
+
elspais/docs/cli/assertions.md,sha256=Rp8KM7s-2JU9A2QA0GtsaL12BTf5gGU_rbOfvMW1gaU,1538
|
|
65
|
+
elspais/docs/cli/commands.md,sha256=41luTLMrl3qvhl_OcEEuAou7BFQpC3--i6D-I8Uut10,8789
|
|
66
|
+
elspais/docs/cli/config.md,sha256=ckRwF2ImcxYeXku-oVomnQrp0xqlvsG7FADdOQMEjfw,6075
|
|
67
|
+
elspais/docs/cli/format.md,sha256=ygsfxdIYvWMGoO-8DzlY8ELATlaUJhGNxfLe77ehljM,1633
|
|
68
|
+
elspais/docs/cli/git.md,sha256=kXW5WaVu9XkzgQ0ryBl4H-Vav2sVGThH_DifdvBqQFs,1284
|
|
69
|
+
elspais/docs/cli/health.md,sha256=mFLUEOhcSfZ6aHMrzYjqUbcDhMmaieMIaEQaVD4DqUo,5146
|
|
70
|
+
elspais/docs/cli/hierarchy.md,sha256=_hqxQVu-sp_BhJnLznKljiTDZDMNXKYqMh-uDyjVuB0,1677
|
|
71
|
+
elspais/docs/cli/ignore.md,sha256=THa2hdt05Hq4Bi3eKdt4prDUntU1xV3rySldY2DLhNw,2097
|
|
72
|
+
elspais/docs/cli/mcp.md,sha256=4UrxT6QSC-qgtz_pkWcWarpnDVRBb8hemK9jBnYFi6o,6564
|
|
73
|
+
elspais/docs/cli/quickstart.md,sha256=y7rpVjN3lcL32517rEpNF0b2Nw8IAH5xMCFh02wRK74,1412
|
|
74
|
+
elspais/docs/cli/traceability.md,sha256=CtZfFUqyWNqtAgL3yQYGT8OUpsof0tkzTturRk4vzJ0,2573
|
|
75
|
+
elspais/docs/cli/validation.md,sha256=LYnL3YaSOhdx-kBGOUYUxD9yYDUasX41RhuO7BolnWs,2535
|
|
76
|
+
elspais-0.43.5.dist-info/METADATA,sha256=HBl9hNKnkh9Q-xRm1JP6m5U6ZKMm9bRHWwh9bPmSSJE,11767
|
|
77
|
+
elspais-0.43.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
78
|
+
elspais-0.43.5.dist-info/entry_points.txt,sha256=yWZZEfn2fBSKSzGoS-fMQ9YoTkyeu6-i7Oht6NsdKpk,45
|
|
79
|
+
elspais-0.43.5.dist-info/licenses/LICENSE,sha256=x_dNMsy_askp2MmKXZFL2bKW_tDiJHcRTyAg0TY1RMI,1063
|
|
80
|
+
elspais-0.43.5.dist-info/RECORD,,
|
elspais/config/defaults.py
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
elspais.config.defaults - Default configuration values.
|
|
3
|
-
|
|
4
|
-
Provides built-in defaults matching the HHT-diary repository structure.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
DEFAULT_CONFIG = {
|
|
8
|
-
"project": {
|
|
9
|
-
"name": "",
|
|
10
|
-
"type": "core",
|
|
11
|
-
},
|
|
12
|
-
"directories": {
|
|
13
|
-
"spec": "spec",
|
|
14
|
-
"docs": "docs",
|
|
15
|
-
"database": "database",
|
|
16
|
-
"code": ["apps", "packages", "server", "tools"],
|
|
17
|
-
"ignore": [
|
|
18
|
-
"node_modules",
|
|
19
|
-
".git",
|
|
20
|
-
"build",
|
|
21
|
-
"dist",
|
|
22
|
-
".dart_tool",
|
|
23
|
-
"__pycache__",
|
|
24
|
-
".venv",
|
|
25
|
-
"venv",
|
|
26
|
-
],
|
|
27
|
-
},
|
|
28
|
-
"patterns": {
|
|
29
|
-
"id_template": "{prefix}-{associated}{type}{id}",
|
|
30
|
-
"prefix": "REQ",
|
|
31
|
-
"types": {
|
|
32
|
-
"prd": {"id": "p", "name": "Product Requirement", "level": 1},
|
|
33
|
-
"ops": {"id": "o", "name": "Operations Requirement", "level": 2},
|
|
34
|
-
"dev": {"id": "d", "name": "Development Requirement", "level": 3},
|
|
35
|
-
},
|
|
36
|
-
"id_format": {
|
|
37
|
-
"style": "numeric",
|
|
38
|
-
"digits": 5,
|
|
39
|
-
"leading_zeros": True,
|
|
40
|
-
},
|
|
41
|
-
"associated": {
|
|
42
|
-
"enabled": True,
|
|
43
|
-
"position": "after_prefix",
|
|
44
|
-
"format": "uppercase",
|
|
45
|
-
"length": 3,
|
|
46
|
-
"separator": "-",
|
|
47
|
-
},
|
|
48
|
-
"assertions": {
|
|
49
|
-
"label_style": "uppercase", # uppercase | numeric | alphanumeric | numeric_1based
|
|
50
|
-
"max_count": 26,
|
|
51
|
-
"zero_pad": False,
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
"spec": {
|
|
55
|
-
"index_file": "INDEX.md",
|
|
56
|
-
"readme_file": "README.md",
|
|
57
|
-
"format_guide": "requirements-format.md",
|
|
58
|
-
"skip_files": ["README.md", "requirements-format.md", "INDEX.md"],
|
|
59
|
-
"file_patterns": {
|
|
60
|
-
"prd-*.md": "prd",
|
|
61
|
-
"ops-*.md": "ops",
|
|
62
|
-
"dev-*.md": "dev",
|
|
63
|
-
},
|
|
64
|
-
# Values in Implements field that mean "no references"
|
|
65
|
-
"no_reference_values": ["-", "null", "none", "x", "X", "N/A", "n/a"],
|
|
66
|
-
},
|
|
67
|
-
"core": {
|
|
68
|
-
"path": None,
|
|
69
|
-
"remote": None,
|
|
70
|
-
},
|
|
71
|
-
"associated": {
|
|
72
|
-
"prefix": None,
|
|
73
|
-
"id_range": [1, 99999],
|
|
74
|
-
},
|
|
75
|
-
"rules": {
|
|
76
|
-
"hierarchy": {
|
|
77
|
-
"allowed_implements": [
|
|
78
|
-
"dev -> ops, prd",
|
|
79
|
-
"ops -> prd",
|
|
80
|
-
"prd -> prd",
|
|
81
|
-
],
|
|
82
|
-
"allow_circular": False,
|
|
83
|
-
"allow_orphans": False,
|
|
84
|
-
"max_depth": 5,
|
|
85
|
-
"cross_repo_implements": True,
|
|
86
|
-
},
|
|
87
|
-
"format": {
|
|
88
|
-
"require_hash": True,
|
|
89
|
-
"require_rationale": False,
|
|
90
|
-
"require_status": True,
|
|
91
|
-
"allowed_statuses": ["Active", "Draft", "Deprecated", "Superseded"],
|
|
92
|
-
# Assertion format rules
|
|
93
|
-
"require_assertions": True,
|
|
94
|
-
"acceptance_criteria": "warn", # allow | warn | error
|
|
95
|
-
"require_shall": True,
|
|
96
|
-
"labels_sequential": True,
|
|
97
|
-
"labels_unique": True,
|
|
98
|
-
"placeholder_values": [
|
|
99
|
-
"obsolete",
|
|
100
|
-
"removed",
|
|
101
|
-
"deprecated",
|
|
102
|
-
"N/A",
|
|
103
|
-
"n/a",
|
|
104
|
-
"-",
|
|
105
|
-
"reserved",
|
|
106
|
-
],
|
|
107
|
-
},
|
|
108
|
-
"traceability": {
|
|
109
|
-
"require_code_link": False,
|
|
110
|
-
"scan_for_orphans": True,
|
|
111
|
-
},
|
|
112
|
-
"naming": {
|
|
113
|
-
"title_min_length": 10,
|
|
114
|
-
"title_max_length": 100,
|
|
115
|
-
"title_pattern": "^[A-Z].*",
|
|
116
|
-
},
|
|
117
|
-
"content_rules": [], # List of content rule markdown file paths
|
|
118
|
-
},
|
|
119
|
-
"validation": {
|
|
120
|
-
"strict_hierarchy": True,
|
|
121
|
-
"hash_algorithm": "sha256",
|
|
122
|
-
"hash_length": 8,
|
|
123
|
-
"normalize_whitespace": False, # If True, normalize whitespace before hashing
|
|
124
|
-
},
|
|
125
|
-
"traceability": {
|
|
126
|
-
"output_formats": ["markdown", "html"],
|
|
127
|
-
"output_dir": ".",
|
|
128
|
-
"scan_patterns": [
|
|
129
|
-
"database/**/*.sql",
|
|
130
|
-
"apps/**/*.dart",
|
|
131
|
-
"packages/**/*.dart",
|
|
132
|
-
"server/**/*.dart",
|
|
133
|
-
"tools/**/*.py",
|
|
134
|
-
".github/workflows/**/*.yml",
|
|
135
|
-
],
|
|
136
|
-
"impl_patterns": [
|
|
137
|
-
r"IMPLEMENTS.*REQ-",
|
|
138
|
-
r"Implements:\s*REQ-",
|
|
139
|
-
r"Fixes:\s*REQ-",
|
|
140
|
-
],
|
|
141
|
-
},
|
|
142
|
-
"index": {
|
|
143
|
-
"auto_regenerate": False,
|
|
144
|
-
},
|
|
145
|
-
"testing": {
|
|
146
|
-
"enabled": False,
|
|
147
|
-
"test_dirs": [
|
|
148
|
-
"apps/**/test",
|
|
149
|
-
"apps/**/tests",
|
|
150
|
-
"packages/**/test",
|
|
151
|
-
"packages/**/tests",
|
|
152
|
-
"tools/**/tests",
|
|
153
|
-
"tests",
|
|
154
|
-
],
|
|
155
|
-
"patterns": [
|
|
156
|
-
"*_test.dart",
|
|
157
|
-
"test_*.dart",
|
|
158
|
-
"test_*.py",
|
|
159
|
-
"*_test.py",
|
|
160
|
-
"*_test.sql",
|
|
161
|
-
],
|
|
162
|
-
"result_files": [
|
|
163
|
-
"build-reports/**/TEST-*.xml",
|
|
164
|
-
"build-reports/pytest-results.json",
|
|
165
|
-
],
|
|
166
|
-
"reference_patterns": [
|
|
167
|
-
# Test function names containing requirement IDs
|
|
168
|
-
r"test_.*(?:REQ[-_])?([pod]\d{5})(?:_[A-Z])?",
|
|
169
|
-
# Comment/docstring patterns
|
|
170
|
-
r"(?:IMPLEMENTS|Implements|implements)[:\s]+(?:REQ[-_])?([pod]\d{5})(?:-[A-Z])?",
|
|
171
|
-
# Direct requirement ID mentions
|
|
172
|
-
r"\bREQ[-_]([pod]\d{5})(?:-[A-Z])?\b",
|
|
173
|
-
],
|
|
174
|
-
},
|
|
175
|
-
"hooks": {
|
|
176
|
-
"pre_commit": True,
|
|
177
|
-
"commit_msg": True,
|
|
178
|
-
},
|
|
179
|
-
}
|