aero-agent-roles 1.0.0
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.
- package/NOTICE +18 -0
- package/README.md +49 -0
- package/bin/aero-agent-roles.js +127 -0
- package/lib/catalog.js +97 -0
- package/lib/install.js +48 -0
- package/lib/mcp.js +199 -0
- package/manifest.json +516 -0
- package/package.json +44 -0
- package/roles/aerodynamics-engineer/ROLE.md +153 -0
- package/roles/aerodynamics-engineer/SOURCES.md +12 -0
- package/roles/aerodynamics-engineer/cli.py +143 -0
- package/roles/aerodynamics-engineer/core/aerodynamics_core.py +1218 -0
- package/roles/aerodynamics-engineer/templates/aero-design-report-template.md +72 -0
- package/roles/aerodynamics-engineer/tests/test_aerodynamics_core.py +227 -0
- package/roles/aerodynamics-engineer/tests/test_role_aerodynamics.py +94 -0
- package/roles/aircraft-design-engineer/ROLE.md +138 -0
- package/roles/aircraft-design-engineer/SOURCES.md +12 -0
- package/roles/aircraft-design-engineer/cli.py +152 -0
- package/roles/aircraft-design-engineer/core/aircraft_design_core.py +1278 -0
- package/roles/aircraft-design-engineer/templates/concept-design-template.md +99 -0
- package/roles/aircraft-design-engineer/tests/test_aircraft_design_core.py +269 -0
- package/roles/aircraft-design-engineer/tests/test_role_aircraft_design_engineer.py +106 -0
- package/roles/airworthiness-compliance-engineer/ROLE.md +122 -0
- package/roles/airworthiness-compliance-engineer/SOURCES.md +16 -0
- package/roles/airworthiness-compliance-engineer/cli.py +146 -0
- package/roles/airworthiness-compliance-engineer/core/airworthiness_core.py +894 -0
- package/roles/airworthiness-compliance-engineer/templates/compliance-matrix-template.md +46 -0
- package/roles/airworthiness-compliance-engineer/tests/test_airworthiness_core.py +224 -0
- package/roles/airworthiness-compliance-engineer/tests/test_role_airworthiness.py +85 -0
- package/roles/as9100-quality-auditor/ROLE.md +128 -0
- package/roles/as9100-quality-auditor/SOURCES.md +16 -0
- package/roles/as9100-quality-auditor/cli.py +136 -0
- package/roles/as9100-quality-auditor/core/as9100_core.py +857 -0
- package/roles/as9100-quality-auditor/templates/findings-report-template.md +119 -0
- package/roles/as9100-quality-auditor/tests/test_as9100_cli_profile.py +105 -0
- package/roles/as9100-quality-auditor/tests/test_as9100_core.py +204 -0
- package/roles/as9100-quality-auditor/tests/test_role_as9100.py +90 -0
- package/roles/data-bus-avionics-engineer/ROLE.md +146 -0
- package/roles/data-bus-avionics-engineer/SOURCES.md +23 -0
- package/roles/data-bus-avionics-engineer/cli.py +311 -0
- package/roles/data-bus-avionics-engineer/core/data_bus_avionics_core.py +1121 -0
- package/roles/data-bus-avionics-engineer/templates/bus-assessment-template.md +134 -0
- package/roles/data-bus-avionics-engineer/tests/test_bundle_protocol.py +137 -0
- package/roles/data-bus-avionics-engineer/tests/test_data_bus_avionics_core.py +261 -0
- package/roles/data-bus-avionics-engineer/tests/test_role_data_bus_avionics_engineer.py +121 -0
- package/roles/do160-environmental-engineer/ROLE.md +136 -0
- package/roles/do160-environmental-engineer/SOURCES.md +30 -0
- package/roles/do160-environmental-engineer/cli.py +147 -0
- package/roles/do160-environmental-engineer/core/do160_environmental_core.py +1246 -0
- package/roles/do160-environmental-engineer/templates/environmental-qualification-plan-template.md +114 -0
- package/roles/do160-environmental-engineer/tests/test_do160_environmental_core.py +312 -0
- package/roles/do160-environmental-engineer/tests/test_role_do160_environmental_engineer.py +281 -0
- package/roles/do178c-cert-engineer/ROLE.md +120 -0
- package/roles/do178c-cert-engineer/SOURCES.md +16 -0
- package/roles/do178c-cert-engineer/cli.py +129 -0
- package/roles/do178c-cert-engineer/core/do178c_core.py +411 -0
- package/roles/do178c-cert-engineer/templates/psac-template.md +74 -0
- package/roles/do178c-cert-engineer/tests/test_do178c_cert_engineer_core.py +91 -0
- package/roles/do178c-cert-engineer/tests/test_role_do178c.py +109 -0
- package/roles/do254-hardware-engineer/ROLE.md +127 -0
- package/roles/do254-hardware-engineer/SOURCES.md +19 -0
- package/roles/do254-hardware-engineer/cli.py +308 -0
- package/roles/do254-hardware-engineer/core/do254_hardware_core.py +761 -0
- package/roles/do254-hardware-engineer/templates/phac-template.md +87 -0
- package/roles/do254-hardware-engineer/tests/test_do254_bundle_profile.py +152 -0
- package/roles/do254-hardware-engineer/tests/test_do254_hardware_core.py +200 -0
- package/roles/do254-hardware-engineer/tests/test_role_do254_hardware.py +108 -0
- package/roles/engineering-analysis-engineer/ROLE.md +154 -0
- package/roles/engineering-analysis-engineer/SOURCES.md +15 -0
- package/roles/engineering-analysis-engineer/cli.py +151 -0
- package/roles/engineering-analysis-engineer/core/engineering_analysis_engineer_core.py +866 -0
- package/roles/engineering-analysis-engineer/templates/analysis-memo-template.md +73 -0
- package/roles/engineering-analysis-engineer/tests/test_engineering_analysis_engineer_core.py +367 -0
- package/roles/engineering-analysis-engineer/tests/test_role_engineering_analysis_engineer.py +113 -0
- package/roles/flight-management-engineer/ROLE.md +160 -0
- package/roles/flight-management-engineer/SOURCES.md +30 -0
- package/roles/flight-management-engineer/cli.py +258 -0
- package/roles/flight-management-engineer/core/flight_management_core.py +1473 -0
- package/roles/flight-management-engineer/templates/route-assessment-template.md +94 -0
- package/roles/flight-management-engineer/tests/test_flight_management_core.py +437 -0
- package/roles/flight-management-engineer/tests/test_role_flight_management_engineer.py +123 -0
- package/roles/flight-mechanics-engineer/ROLE.md +162 -0
- package/roles/flight-mechanics-engineer/SOURCES.md +51 -0
- package/roles/flight-mechanics-engineer/cli.py +149 -0
- package/roles/flight-mechanics-engineer/core/flight_mechanics_core.py +1507 -0
- package/roles/flight-mechanics-engineer/templates/perf-sc-report-template.md +82 -0
- package/roles/flight-mechanics-engineer/tests/test_flight_mechanics_core.py +277 -0
- package/roles/flight-mechanics-engineer/tests/test_role_flight_mechanics_engineer.py +141 -0
- package/roles/flight-test-engineer/ROLE.md +137 -0
- package/roles/flight-test-engineer/SOURCES.md +24 -0
- package/roles/flight-test-engineer/cli.py +135 -0
- package/roles/flight-test-engineer/core/flight_test_core.py +931 -0
- package/roles/flight-test-engineer/templates/flight-test-template.md +143 -0
- package/roles/flight-test-engineer/tests/test_flight_test_engineer_core.py +227 -0
- package/roles/flight-test-engineer/tests/test_role_flight_test_engineer.py +150 -0
- package/roles/flight-test-performance-engineer/ROLE.md +165 -0
- package/roles/flight-test-performance-engineer/SOURCES.md +20 -0
- package/roles/flight-test-performance-engineer/cli.py +319 -0
- package/roles/flight-test-performance-engineer/core/flight_test_performance_core.py +1398 -0
- package/roles/flight-test-performance-engineer/templates/performance-report-template.md +105 -0
- package/roles/flight-test-performance-engineer/tests/test_flight_test_performance_engineer_core.py +308 -0
- package/roles/flight-test-performance-engineer/tests/test_role_flight_test_performance_engineer.py +165 -0
- package/roles/gnc-engineer/ROLE.md +141 -0
- package/roles/gnc-engineer/SOURCES.md +35 -0
- package/roles/gnc-engineer/cli.py +141 -0
- package/roles/gnc-engineer/core/gnc_core.py +1074 -0
- package/roles/gnc-engineer/templates/gnc-report-template.md +105 -0
- package/roles/gnc-engineer/tests/test_gnc_engineer_core.py +238 -0
- package/roles/gnc-engineer/tests/test_role_gnc_engineer.py +142 -0
- package/roles/guidance-engineer/ROLE.md +156 -0
- package/roles/guidance-engineer/SOURCES.md +43 -0
- package/roles/guidance-engineer/cli.py +280 -0
- package/roles/guidance-engineer/core/guidance_core.py +820 -0
- package/roles/guidance-engineer/templates/guidance-report-template.md +70 -0
- package/roles/guidance-engineer/tests/test_guidance_engineer_core.py +269 -0
- package/roles/guidance-engineer/tests/test_role_guidance_engineer.py +146 -0
- package/roles/high-speed-aerodynamics-engineer/ROLE.md +161 -0
- package/roles/high-speed-aerodynamics-engineer/SOURCES.md +18 -0
- package/roles/high-speed-aerodynamics-engineer/cli.py +282 -0
- package/roles/high-speed-aerodynamics-engineer/core/high_speed_aero_core.py +1566 -0
- package/roles/high-speed-aerodynamics-engineer/templates/high-speed-memo-template.md +75 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_bundle_protocol.py +103 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_high_speed_aero_core.py +280 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_role_high_speed_aerodynamics.py +125 -0
- package/roles/ndt-engineer/ROLE.md +157 -0
- package/roles/ndt-engineer/SOURCES.md +18 -0
- package/roles/ndt-engineer/cli.py +316 -0
- package/roles/ndt-engineer/core/ndt_core.py +1590 -0
- package/roles/ndt-engineer/templates/ndt-plan-template.md +109 -0
- package/roles/ndt-engineer/tests/test_ndt_cli_bundle.py +183 -0
- package/roles/ndt-engineer/tests/test_ndt_core.py +437 -0
- package/roles/ndt-engineer/tests/test_role_ndt_engineer.py +148 -0
- package/roles/numerical-analysis-engineer/ROLE.md +158 -0
- package/roles/numerical-analysis-engineer/SOURCES.md +19 -0
- package/roles/numerical-analysis-engineer/cli.py +414 -0
- package/roles/numerical-analysis-engineer/core/numerical_analysis_core.py +1220 -0
- package/roles/numerical-analysis-engineer/templates/verification-memo-template.md +120 -0
- package/roles/numerical-analysis-engineer/tests/test_bundle_protocol.py +157 -0
- package/roles/numerical-analysis-engineer/tests/test_numerical_analysis_core.py +337 -0
- package/roles/numerical-analysis-engineer/tests/test_role_numerical_analysis_engineer.py +117 -0
- package/roles/propulsion-engineer/ROLE.md +163 -0
- package/roles/propulsion-engineer/SOURCES.md +15 -0
- package/roles/propulsion-engineer/cli.py +168 -0
- package/roles/propulsion-engineer/core/propulsion_core.py +1291 -0
- package/roles/propulsion-engineer/templates/propulsion-report-template.md +92 -0
- package/roles/propulsion-engineer/tests/test_propulsion_engineer_core.py +294 -0
- package/roles/propulsion-engineer/tests/test_role_propulsion_engineer.py +106 -0
- package/roles/space-systems-engineer/ROLE.md +161 -0
- package/roles/space-systems-engineer/SOURCES.md +49 -0
- package/roles/space-systems-engineer/cli.py +173 -0
- package/roles/space-systems-engineer/core/space_systems_core.py +980 -0
- package/roles/space-systems-engineer/templates/space-systems-template.md +88 -0
- package/roles/space-systems-engineer/tests/test_role_space_systems_engineer.py +117 -0
- package/roles/space-systems-engineer/tests/test_space_systems_engineer_core.py +247 -0
- package/roles/state-estimation-engineer/ROLE.md +154 -0
- package/roles/state-estimation-engineer/SOURCES.md +27 -0
- package/roles/state-estimation-engineer/cli.py +326 -0
- package/roles/state-estimation-engineer/core/state_estimation_core.py +958 -0
- package/roles/state-estimation-engineer/templates/nav-state-estimator-design-template.md +76 -0
- package/roles/state-estimation-engineer/tests/test_bundle_protocol.py +110 -0
- package/roles/state-estimation-engineer/tests/test_role_state_estimation_engineer.py +127 -0
- package/roles/state-estimation-engineer/tests/test_state_estimation_engineer_core.py +231 -0
- package/roles/structures-loads-engineer/ROLE.md +171 -0
- package/roles/structures-loads-engineer/SOURCES.md +44 -0
- package/roles/structures-loads-engineer/cli.py +219 -0
- package/roles/structures-loads-engineer/core/structures_loads_core.py +1112 -0
- package/roles/structures-loads-engineer/templates/loads-strength-report-template.md +96 -0
- package/roles/structures-loads-engineer/tests/test_bundle_protocol.py +101 -0
- package/roles/structures-loads-engineer/tests/test_role_structures_loads_engineer.py +147 -0
- package/roles/structures-loads-engineer/tests/test_structures_loads_core.py +261 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""do178c_core.py - DO-178C Certification Engineer executable core.
|
|
3
|
+
|
|
4
|
+
This is the role's ENGINE: given a software item's project facts it
|
|
5
|
+
computes DAL requirements, coverage targets, independence, the life
|
|
6
|
+
cycle data set, verification objectives, and BUILDS the Plan for
|
|
7
|
+
Software Aspects of Certification (PSAC) content. It also gate-checks
|
|
8
|
+
deliverables. Standalone: no external repo needed.
|
|
9
|
+
|
|
10
|
+
Domain rules encoded here are public process knowledge (DAL levels,
|
|
11
|
+
coverage metrics, independence practice as published in FAA AC 20-115D /
|
|
12
|
+
EASA AMC 20-115C and standard certification practice). DO-178C text is
|
|
13
|
+
never reproduced - the plan structure is an original synthesis.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import re
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from datetime import date
|
|
21
|
+
|
|
22
|
+
def _today() -> str:
|
|
23
|
+
import os
|
|
24
|
+
from datetime import date
|
|
25
|
+
return os.environ.get("ROLE_GEN_DATE", date.today().isoformat())
|
|
26
|
+
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
# Domain tables (public process knowledge)
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
# Software level from failure-condition severity (FAR 25.1309 / CS-25.1309
|
|
32
|
+
# standard severity categories -> DAL mapping used in certification practice).
|
|
33
|
+
SEVERITY_TO_DAL = {
|
|
34
|
+
"catastrophic": "A",
|
|
35
|
+
"hazardous": "B",
|
|
36
|
+
"major": "C",
|
|
37
|
+
"minor": "D",
|
|
38
|
+
"no-safety-effect": "E",
|
|
39
|
+
}
|
|
40
|
+
DALS = ("A", "B", "C", "D", "E")
|
|
41
|
+
|
|
42
|
+
# Coverage targets by DAL (requirements-based coverage ratios, as used in
|
|
43
|
+
# verification practice: A/B 0.98, C/D 0.95 per measured requirements).
|
|
44
|
+
COVERAGE_TARGET = {"A": 0.98, "B": 0.98, "C": 0.95, "D": 0.95, "E": None}
|
|
45
|
+
|
|
46
|
+
# Structural coverage requirements by DAL (standard practice): MC/DC at A,
|
|
47
|
+
# decision coverage at B, statement coverage at C.
|
|
48
|
+
STRUCTURAL = {
|
|
49
|
+
"A": ["statement", "decision", "mc-dc", "data-coupling", "control-coupling"],
|
|
50
|
+
"B": ["statement", "decision", "data-coupling", "control-coupling"],
|
|
51
|
+
"C": ["statement"],
|
|
52
|
+
"D": [],
|
|
53
|
+
"E": [],
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Independence expectations by DAL (verification independence for A/B).
|
|
57
|
+
INDEPENDENT = {"A": True, "B": True, "C": False, "D": False, "E": False}
|
|
58
|
+
|
|
59
|
+
# Life cycle data set by DAL: the data items a PSAC must name. Levels with
|
|
60
|
+
# more rigor require the full set; E is minimal. (Original synthesis of the
|
|
61
|
+
# standard plan-of-attack categories, not reproduced text.)
|
|
62
|
+
LIFE_CYCLE_DATA = {
|
|
63
|
+
"A": [
|
|
64
|
+
"plan for software aspects of certification",
|
|
65
|
+
"software development plan",
|
|
66
|
+
"software verification plan",
|
|
67
|
+
"software configuration management plan",
|
|
68
|
+
"software quality assurance plan",
|
|
69
|
+
"software requirements standards",
|
|
70
|
+
"software design standards",
|
|
71
|
+
"software code standards",
|
|
72
|
+
"software requirements data",
|
|
73
|
+
"software design data",
|
|
74
|
+
"software source code",
|
|
75
|
+
"executable object code",
|
|
76
|
+
"software verification results",
|
|
77
|
+
"software life cycle environment configuration index",
|
|
78
|
+
"software configuration index",
|
|
79
|
+
"software accomplishment summary",
|
|
80
|
+
"software conformity review records",
|
|
81
|
+
],
|
|
82
|
+
"B": [
|
|
83
|
+
"plan for software aspects of certification",
|
|
84
|
+
"software development plan",
|
|
85
|
+
"software verification plan",
|
|
86
|
+
"software configuration management plan",
|
|
87
|
+
"software quality assurance plan",
|
|
88
|
+
"software requirements standards",
|
|
89
|
+
"software design standards",
|
|
90
|
+
"software code standards",
|
|
91
|
+
"software requirements data",
|
|
92
|
+
"software design data",
|
|
93
|
+
"software source code",
|
|
94
|
+
"executable object code",
|
|
95
|
+
"software verification results",
|
|
96
|
+
"software life cycle environment configuration index",
|
|
97
|
+
"software configuration index",
|
|
98
|
+
"software accomplishment summary",
|
|
99
|
+
],
|
|
100
|
+
"C": [
|
|
101
|
+
"plan for software aspects of certification",
|
|
102
|
+
"software development plan",
|
|
103
|
+
"software verification plan",
|
|
104
|
+
"software configuration management plan",
|
|
105
|
+
"software quality assurance plan",
|
|
106
|
+
"software requirements data",
|
|
107
|
+
"software design data",
|
|
108
|
+
"software source code",
|
|
109
|
+
"executable object code",
|
|
110
|
+
"software verification results",
|
|
111
|
+
"software life cycle environment configuration index",
|
|
112
|
+
"software configuration index",
|
|
113
|
+
"software accomplishment summary",
|
|
114
|
+
],
|
|
115
|
+
"D": [
|
|
116
|
+
"plan for software aspects of certification",
|
|
117
|
+
"software development plan",
|
|
118
|
+
"software verification plan",
|
|
119
|
+
"software configuration management plan",
|
|
120
|
+
"software quality assurance plan",
|
|
121
|
+
"software requirements data",
|
|
122
|
+
"software design data",
|
|
123
|
+
"software source code",
|
|
124
|
+
"executable object code",
|
|
125
|
+
"software verification results",
|
|
126
|
+
"software life cycle environment configuration index",
|
|
127
|
+
"software configuration index",
|
|
128
|
+
],
|
|
129
|
+
"E": [
|
|
130
|
+
"software requirements data",
|
|
131
|
+
"software design data",
|
|
132
|
+
"software source code",
|
|
133
|
+
"software verification results",
|
|
134
|
+
],
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# Verification objectives by DAL: the objective families expected per level
|
|
138
|
+
# (public practice: completeness/independence/coverage expectations).
|
|
139
|
+
VERIFICATION_FAMILIES = {
|
|
140
|
+
"A": ["planning", "development", "verification", "cm", "sqa", "cert-liaison"],
|
|
141
|
+
"B": ["planning", "development", "verification", "cm", "sqa", "cert-liaison"],
|
|
142
|
+
"C": ["planning", "development", "verification", "cm", "sqa"],
|
|
143
|
+
"D": ["planning", "development", "verification", "cm", "sqa"],
|
|
144
|
+
"E": ["development", "verification"],
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@dataclass
|
|
149
|
+
class SoftwareItem:
|
|
150
|
+
"""Project facts the role needs to build the PSAC."""
|
|
151
|
+
item_name: str
|
|
152
|
+
description: str = ""
|
|
153
|
+
failure_condition: str = "major" # severity category
|
|
154
|
+
system_safety_ref: str = "" # FHA/PSSA reference
|
|
155
|
+
life_cycle_model: str = "waterfall"
|
|
156
|
+
programming_languages: list = field(default_factory=lambda: ["C"])
|
|
157
|
+
target_platform: str = ""
|
|
158
|
+
development_tools: list = field(default_factory=list)
|
|
159
|
+
verification_tools: list = field(default_factory=list)
|
|
160
|
+
tool_qualification_approach: str = "criteria-based per DO-330"
|
|
161
|
+
previously_developed: bool = False
|
|
162
|
+
pds_origin_standard: str = ""
|
|
163
|
+
certification_basis: str = "FAR/CS-25"
|
|
164
|
+
|
|
165
|
+
def software_level(self) -> str:
|
|
166
|
+
return SEVERITY_TO_DAL.get(self.failure_condition.lower(), "E")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# ---------------------------------------------------------------------------
|
|
170
|
+
# Core computations
|
|
171
|
+
# ---------------------------------------------------------------------------
|
|
172
|
+
|
|
173
|
+
def _check_dal(dal: str) -> None:
|
|
174
|
+
if dal not in DALS:
|
|
175
|
+
raise ValueError(f"invalid DAL {dal!r}")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def coverage_target(dal: str) -> float | None:
|
|
179
|
+
_check_dal(dal)
|
|
180
|
+
return COVERAGE_TARGET[dal]
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def structural_coverage(dal: str) -> list[str]:
|
|
184
|
+
_check_dal(dal)
|
|
185
|
+
return STRUCTURAL[dal]
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def independence_required(dal: str) -> bool:
|
|
189
|
+
_check_dal(dal)
|
|
190
|
+
return INDEPENDENT[dal]
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def life_cycle_data(dal: str) -> list[str]:
|
|
194
|
+
_check_dal(dal)
|
|
195
|
+
return LIFE_CYCLE_DATA[dal]
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def coverage_adequate(dal: str, measured_pct: float) -> bool:
|
|
199
|
+
"""True when measured requirements-based coverage meets the DAL target."""
|
|
200
|
+
target = coverage_target(dal)
|
|
201
|
+
if target is None:
|
|
202
|
+
return True
|
|
203
|
+
return measured_pct >= target
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def verification_independence_note(dal: str) -> str:
|
|
207
|
+
return ("independent" if independence_required(dal)
|
|
208
|
+
else "may be conducted by the developer")
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def structural_methods_text(dal: str) -> str:
|
|
212
|
+
cov = structural_coverage(dal)
|
|
213
|
+
if not cov:
|
|
214
|
+
return "No structural coverage required at this level."
|
|
215
|
+
return "Required: " + ", ".join(cov) + "."
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
# ---------------------------------------------------------------------------
|
|
219
|
+
# PSAC builder: produces the actual deliverable content
|
|
220
|
+
# ---------------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
def build_psac(item: SoftwareItem) -> dict:
|
|
223
|
+
"""Build the complete PSAC content model from project facts."""
|
|
224
|
+
dal = item.software_level()
|
|
225
|
+
return {
|
|
226
|
+
"document_type": "Plan for Software Aspects of Certification",
|
|
227
|
+
"status": "draft-for-review",
|
|
228
|
+
"item": item.item_name,
|
|
229
|
+
"item_description": item.description,
|
|
230
|
+
"certification_basis": item.certification_basis,
|
|
231
|
+
"software_level": dal,
|
|
232
|
+
"severity_source": item.failure_condition,
|
|
233
|
+
"system_safety_reference": item.system_safety_ref,
|
|
234
|
+
"life_cycle_model": item.life_cycle_model,
|
|
235
|
+
"coverage_target": coverage_target(dal),
|
|
236
|
+
"structural_coverage": structural_coverage(dal),
|
|
237
|
+
"independence": verification_independence_note(dal),
|
|
238
|
+
"life_cycle_data": life_cycle_data(dal),
|
|
239
|
+
"languages": item.programming_languages,
|
|
240
|
+
"target_platform": item.target_platform,
|
|
241
|
+
"development_tools": item.development_tools,
|
|
242
|
+
"verification_tools": item.verification_tools,
|
|
243
|
+
"tool_qualification_approach": item.tool_qualification_approach,
|
|
244
|
+
"previously_developed": item.previously_developed,
|
|
245
|
+
"pds_origin_standard": item.pds_origin_standard,
|
|
246
|
+
"objectives": VERIFICATION_FAMILIES[dal],
|
|
247
|
+
"generated": _today(),
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def render_psac_markdown(model: dict) -> str:
|
|
252
|
+
"""Render the PSAC content model as the deliverable markdown document."""
|
|
253
|
+
lines = [
|
|
254
|
+
"# Plan for Software Aspects of Certification",
|
|
255
|
+
"",
|
|
256
|
+
f"**Item:** {model['item']}",
|
|
257
|
+
f"**Software level:** {model['software_level']} "
|
|
258
|
+
f"(from {model['severity_source']} failure condition)",
|
|
259
|
+
f"**Certification basis:** {model['certification_basis']}",
|
|
260
|
+
f"**Status:** {model['status']}",
|
|
261
|
+
"",
|
|
262
|
+
"## 1. Scope",
|
|
263
|
+
"",
|
|
264
|
+
f"This plan covers the software item {model['item']}."
|
|
265
|
+
+ (f" {model['item_description']}" if model['item_description'] else ""),
|
|
266
|
+
f"The software level is {model['software_level']} based on the "
|
|
267
|
+
f"{model['severity_source']} failure condition"
|
|
268
|
+
+ (f" (system safety reference {model['system_safety_reference']})"
|
|
269
|
+
if model['system_safety_reference'] else "") + ".",
|
|
270
|
+
"",
|
|
271
|
+
"## 2. Software life cycle",
|
|
272
|
+
"",
|
|
273
|
+
f"The {model['life_cycle_model']} life cycle is used. The life cycle "
|
|
274
|
+
"data set for this level is:",
|
|
275
|
+
"",
|
|
276
|
+
*[f"- {d}" for d in model["life_cycle_data"]],
|
|
277
|
+
"",
|
|
278
|
+
"## 3. Software life cycle environment",
|
|
279
|
+
"",
|
|
280
|
+
f"- Development tools: {', '.join(model['development_tools']) or 'none'}"
|
|
281
|
+
if model["development_tools"] else "- Development tools: to be defined",
|
|
282
|
+
f"- Verification tools: {', '.join(model['verification_tools']) or 'none'}"
|
|
283
|
+
if model["verification_tools"] else "- Verification tools: to be defined",
|
|
284
|
+
f"- Tool qualification: {model['tool_qualification_approach']}",
|
|
285
|
+
"",
|
|
286
|
+
"## 4. Software development standards",
|
|
287
|
+
"",
|
|
288
|
+
"- Requirements standards: project-defined, referenced in the "
|
|
289
|
+
"software development plan.",
|
|
290
|
+
"- Design standards: project-defined.",
|
|
291
|
+
f"- Code standards: {', '.join(model['languages'])} subset and "
|
|
292
|
+
"metrics defined in the software code standard.",
|
|
293
|
+
"",
|
|
294
|
+
"## 5. Software verification strategy",
|
|
295
|
+
"",
|
|
296
|
+
f"- Requirements-based coverage target: "
|
|
297
|
+
f"{model['coverage_target']:.2f} (level {model['software_level']}).",
|
|
298
|
+
f"- Structural coverage: {', '.join(model['structural_coverage']) or 'none required'}.",
|
|
299
|
+
f"- Verification independence: {model['independence']}.",
|
|
300
|
+
"",
|
|
301
|
+
"## 6. Software configuration management",
|
|
302
|
+
"",
|
|
303
|
+
"The configuration management plan defines baselines, change "
|
|
304
|
+
"control, archiving, and the configuration index for this item.",
|
|
305
|
+
"",
|
|
306
|
+
"## 7. Software quality assurance",
|
|
307
|
+
"",
|
|
308
|
+
"The software quality assurance plan defines SQA process, audits, "
|
|
309
|
+
"and conformity review for this level.",
|
|
310
|
+
"",
|
|
311
|
+
"## 8. Liaison with airworthiness",
|
|
312
|
+
"",
|
|
313
|
+
"Certification authority liaison and the means of compliance are "
|
|
314
|
+
"defined in the certification plan.",
|
|
315
|
+
"",
|
|
316
|
+
"## 9. Objectives summary",
|
|
317
|
+
"",
|
|
318
|
+
*[f"- {o}" for o in model["objectives"]],
|
|
319
|
+
"",
|
|
320
|
+
"---",
|
|
321
|
+
f"*Generated by Aero Agent Roles do178c-cert-engineer core "
|
|
322
|
+
f"({model['generated']}). DRAFT for human certification engineer "
|
|
323
|
+
"review. Not an approval document.*",
|
|
324
|
+
]
|
|
325
|
+
return "\n".join(lines)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
# ---------------------------------------------------------------------------
|
|
329
|
+
# Evidence gate checkers: verify a deliverable meets the role's gates
|
|
330
|
+
# ---------------------------------------------------------------------------
|
|
331
|
+
|
|
332
|
+
GATE_CHECKS = {
|
|
333
|
+
"level_identified": "software_level is a single valid DAL letter",
|
|
334
|
+
"coverage_target_present": "requirements-based coverage target is a number",
|
|
335
|
+
"structural_stated": "structural coverage list is present for the DAL",
|
|
336
|
+
"life_cycle_data_present": "life cycle data set is non-empty and level-appropriate",
|
|
337
|
+
"sign_off_honest": "document is marked draft-for-review, not approval",
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def check_psac(model: dict) -> dict:
|
|
342
|
+
"""Run the evidence gates against a PSAC model. Returns pass/fail per gate."""
|
|
343
|
+
dal = model.get("software_level", "")
|
|
344
|
+
results = {
|
|
345
|
+
"level_identified": dal in DALS,
|
|
346
|
+
"coverage_target_present": isinstance(model.get("coverage_target"), (int, float)),
|
|
347
|
+
"structural_stated": isinstance(model.get("structural_coverage"), list),
|
|
348
|
+
"life_cycle_data_present": bool(model.get("life_cycle_data")),
|
|
349
|
+
"sign_off_honest": model.get("status") == "draft-for-review",
|
|
350
|
+
}
|
|
351
|
+
results["all_pass"] = all(results.values())
|
|
352
|
+
return results
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def check_psac_markdown(md_text: str, dal: str) -> dict:
|
|
356
|
+
"""Gate-check the rendered markdown deliverable."""
|
|
357
|
+
low = md_text.lower()
|
|
358
|
+
level_pat = re.compile(r"software level[:\*]*\s*" + dal.lower() + r"\b")
|
|
359
|
+
checks = {
|
|
360
|
+
"has_title": "plan for software aspects of certification" in low,
|
|
361
|
+
"has_level": bool(level_pat.search(low)),
|
|
362
|
+
"has_coverage": "coverage target" in low,
|
|
363
|
+
"has_draft_marker": "draft" in low,
|
|
364
|
+
"has_not_approval": "not an approval" in low,
|
|
365
|
+
}
|
|
366
|
+
checks["all_pass"] = all(checks.values())
|
|
367
|
+
return checks
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def validate_deliverable_text(md_text: str, dal: str) -> dict:
|
|
371
|
+
"""Public entry point used by gate tooling: check a PSAC document."""
|
|
372
|
+
return check_psac_markdown(md_text, dal)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
# ---------------------------------------------------------------------------
|
|
376
|
+
# Example project (for tests and worked-example generation)
|
|
377
|
+
# ---------------------------------------------------------------------------
|
|
378
|
+
|
|
379
|
+
def example_item() -> SoftwareItem:
|
|
380
|
+
return SoftwareItem(
|
|
381
|
+
item_name="Autopilot Flight Guidance System (FGS) software",
|
|
382
|
+
description="DAL-B autopilot software hosted on the flight "
|
|
383
|
+
"guidance computer.",
|
|
384
|
+
failure_condition="hazardous",
|
|
385
|
+
system_safety_ref="FHA-FGS-001 / PSSA rev C",
|
|
386
|
+
life_cycle_model="waterfall",
|
|
387
|
+
programming_languages=["C"],
|
|
388
|
+
target_platform="PowerPC-based flight guidance computer",
|
|
389
|
+
development_tools=["host compiler", "static analyzer"],
|
|
390
|
+
verification_tools=["requirements-based test harness",
|
|
391
|
+
"code coverage tool"],
|
|
392
|
+
tool_qualification_approach="criteria-based qualification per DO-330",
|
|
393
|
+
certification_basis="FAR/CS-25",
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def example_psac_markdown() -> str:
|
|
398
|
+
return render_psac_markdown(build_psac(example_item()))
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
if __name__ == "__main__":
|
|
402
|
+
import sys
|
|
403
|
+
item = example_item()
|
|
404
|
+
model = build_psac(item)
|
|
405
|
+
md = render_psac_markdown(model)
|
|
406
|
+
print(f"SOFTWARE LEVEL: {model['software_level']}")
|
|
407
|
+
print(f"COVERAGE TARGET: {model['coverage_target']:.2f}")
|
|
408
|
+
print(f"INDEPENDENCE: {model['independence']}")
|
|
409
|
+
print(f"LIFE CYCLE DATA: {len(model['life_cycle_data'])} items")
|
|
410
|
+
print(f"GATES: {check_psac(model)}")
|
|
411
|
+
print(f"RENDERED: {len(md)} chars")
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Plan for Software Aspects of Certification
|
|
2
|
+
|
|
3
|
+
**Item:** Autopilot Flight Guidance System (FGS) software
|
|
4
|
+
**Software level:** B (from hazardous failure condition)
|
|
5
|
+
**Certification basis:** FAR/CS-25
|
|
6
|
+
**Status:** draft-for-review
|
|
7
|
+
|
|
8
|
+
## 1. Scope
|
|
9
|
+
|
|
10
|
+
This plan covers the software item Autopilot Flight Guidance System (FGS) software. DAL-B autopilot software hosted on the flight guidance computer.
|
|
11
|
+
The software level is B based on the hazardous failure condition (system safety reference FHA-FGS-001 / PSSA rev C).
|
|
12
|
+
|
|
13
|
+
## 2. Software life cycle
|
|
14
|
+
|
|
15
|
+
The waterfall life cycle is used. The life cycle data set for this level is:
|
|
16
|
+
|
|
17
|
+
- plan for software aspects of certification
|
|
18
|
+
- software development plan
|
|
19
|
+
- software verification plan
|
|
20
|
+
- software configuration management plan
|
|
21
|
+
- software quality assurance plan
|
|
22
|
+
- software requirements standards
|
|
23
|
+
- software design standards
|
|
24
|
+
- software code standards
|
|
25
|
+
- software requirements data
|
|
26
|
+
- software design data
|
|
27
|
+
- software source code
|
|
28
|
+
- executable object code
|
|
29
|
+
- software verification results
|
|
30
|
+
- software life cycle environment configuration index
|
|
31
|
+
- software configuration index
|
|
32
|
+
- software accomplishment summary
|
|
33
|
+
|
|
34
|
+
## 3. Software life cycle environment
|
|
35
|
+
|
|
36
|
+
- Development tools: host compiler, static analyzer
|
|
37
|
+
- Verification tools: requirements-based test harness, code coverage tool
|
|
38
|
+
- Tool qualification: criteria-based qualification per DO-330
|
|
39
|
+
|
|
40
|
+
## 4. Software development standards
|
|
41
|
+
|
|
42
|
+
- Requirements standards: project-defined, referenced in the software development plan.
|
|
43
|
+
- Design standards: project-defined.
|
|
44
|
+
- Code standards: C subset and metrics defined in the software code standard.
|
|
45
|
+
|
|
46
|
+
## 5. Software verification strategy
|
|
47
|
+
|
|
48
|
+
- Requirements-based coverage target: 0.98 (level B).
|
|
49
|
+
- Structural coverage: statement, decision, data-coupling, control-coupling.
|
|
50
|
+
- Verification independence: independent.
|
|
51
|
+
|
|
52
|
+
## 6. Software configuration management
|
|
53
|
+
|
|
54
|
+
The configuration management plan defines baselines, change control, archiving, and the configuration index for this item.
|
|
55
|
+
|
|
56
|
+
## 7. Software quality assurance
|
|
57
|
+
|
|
58
|
+
The software quality assurance plan defines SQA process, audits, and conformity review for this level.
|
|
59
|
+
|
|
60
|
+
## 8. Liaison with airworthiness
|
|
61
|
+
|
|
62
|
+
Certification authority liaison and the means of compliance are defined in the certification plan.
|
|
63
|
+
|
|
64
|
+
## 9. Objectives summary
|
|
65
|
+
|
|
66
|
+
- planning
|
|
67
|
+
- development
|
|
68
|
+
- verification
|
|
69
|
+
- cm
|
|
70
|
+
- sqa
|
|
71
|
+
- cert-liaison
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
*Generated by Aero Agent Roles do178c-cert-engineer core (2026-09-05). DRAFT for human certification engineer review. Not an approval document.*
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Test do178c_core: the executable engine of the DO-178C role.
|
|
3
|
+
|
|
4
|
+
Proves the role can DO its job standalone (no AeroSkills needed):
|
|
5
|
+
DAL determination, coverage targets, independence, life cycle data,
|
|
6
|
+
PSAC generation, and evidence-gate checks.
|
|
7
|
+
"""
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
import unittest
|
|
11
|
+
|
|
12
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "core"))
|
|
13
|
+
from do178c_core import (
|
|
14
|
+
SEVERITY_TO_DAL, build_psac, check_psac, check_psac_markdown,
|
|
15
|
+
coverage_adequate, coverage_target, example_item,
|
|
16
|
+
example_psac_markdown, independence_required, life_cycle_data,
|
|
17
|
+
render_psac_markdown, structural_coverage,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TestCoreDomainRules(unittest.TestCase):
|
|
22
|
+
|
|
23
|
+
def test_severity_to_dal_mapping(self):
|
|
24
|
+
self.assertEqual(SEVERITY_TO_DAL["catastrophic"], "A")
|
|
25
|
+
self.assertEqual(SEVERITY_TO_DAL["hazardous"], "B")
|
|
26
|
+
self.assertEqual(SEVERITY_TO_DAL["major"], "C")
|
|
27
|
+
self.assertEqual(SEVERITY_TO_DAL["minor"], "D")
|
|
28
|
+
|
|
29
|
+
def test_coverage_targets(self):
|
|
30
|
+
self.assertEqual(coverage_target("A"), 0.98)
|
|
31
|
+
self.assertEqual(coverage_target("C"), 0.95)
|
|
32
|
+
self.assertIsNone(coverage_target("E"))
|
|
33
|
+
|
|
34
|
+
def test_structural_by_dal(self):
|
|
35
|
+
self.assertIn("mc-dc", structural_coverage("A"))
|
|
36
|
+
self.assertIn("decision", structural_coverage("B"))
|
|
37
|
+
self.assertEqual(structural_coverage("C"), ["statement"])
|
|
38
|
+
self.assertEqual(structural_coverage("D"), [])
|
|
39
|
+
|
|
40
|
+
def test_independence(self):
|
|
41
|
+
self.assertTrue(independence_required("A"))
|
|
42
|
+
self.assertTrue(independence_required("B"))
|
|
43
|
+
self.assertFalse(independence_required("C"))
|
|
44
|
+
|
|
45
|
+
def test_life_cycle_scope(self):
|
|
46
|
+
self.assertGreater(len(life_cycle_data("A")), len(life_cycle_data("D")))
|
|
47
|
+
self.assertIn("plan for software aspects of certification",
|
|
48
|
+
life_cycle_data("B"))
|
|
49
|
+
|
|
50
|
+
def test_coverage_adequate(self):
|
|
51
|
+
self.assertTrue(coverage_adequate("A", 0.99))
|
|
52
|
+
self.assertFalse(coverage_adequate("A", 0.97))
|
|
53
|
+
self.assertTrue(coverage_adequate("E", 0.0)) # no target
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TestPsacBuilder(unittest.TestCase):
|
|
57
|
+
|
|
58
|
+
def test_example_level_b(self):
|
|
59
|
+
model = build_psac(example_item())
|
|
60
|
+
self.assertEqual(model["software_level"], "B")
|
|
61
|
+
self.assertEqual(model["coverage_target"], 0.98)
|
|
62
|
+
self.assertEqual(model["status"], "draft-for-review")
|
|
63
|
+
|
|
64
|
+
def test_psac_has_required_sections(self):
|
|
65
|
+
md = example_psac_markdown()
|
|
66
|
+
for sec in ["## 1. Scope", "## 5. Software verification strategy",
|
|
67
|
+
"## 6. Software configuration management", "## 9. Objectives"]:
|
|
68
|
+
self.assertIn(sec, md)
|
|
69
|
+
self.assertIn("not an approval", md.lower())
|
|
70
|
+
|
|
71
|
+
def test_core_gates_all_pass(self):
|
|
72
|
+
model = build_psac(example_item())
|
|
73
|
+
gates = check_psac(model)
|
|
74
|
+
self.assertTrue(gates["all_pass"], gates)
|
|
75
|
+
|
|
76
|
+
def test_markdown_gates_all_pass(self):
|
|
77
|
+
md = example_psac_markdown()
|
|
78
|
+
gates = check_psac_markdown(md, "B")
|
|
79
|
+
self.assertTrue(gates["all_pass"], gates)
|
|
80
|
+
|
|
81
|
+
def test_standalone_no_skills_repo(self):
|
|
82
|
+
# Core must work with no AeroSkills present: set env to a bad path.
|
|
83
|
+
os.environ["AEROSKILLS_DEV"] = "/nonexistent"
|
|
84
|
+
model = build_psac(example_item())
|
|
85
|
+
self.assertTrue(model["software_level"] in "ABCDE")
|
|
86
|
+
md = render_psac_markdown(model)
|
|
87
|
+
self.assertGreater(len(md), 1000)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
unittest.main()
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Role test: DO-178C Certification Engineer.
|
|
3
|
+
|
|
4
|
+
Verifies (offline, no network):
|
|
5
|
+
1. Every skill bound in ROLE.md frontmatter RESOLVES to a real leaf in
|
|
6
|
+
the aero-agent-skills repo (the pinned skills_release band).
|
|
7
|
+
2. The workflow stage order is deterministic and complete.
|
|
8
|
+
3. Evidence-gate smoke: a synthetic project (mock requirements + tests)
|
|
9
|
+
produces a complete PSAC skeleton with no blank sections.
|
|
10
|
+
"""
|
|
11
|
+
import os
|
|
12
|
+
import re
|
|
13
|
+
import sys
|
|
14
|
+
import tempfile
|
|
15
|
+
import unittest
|
|
16
|
+
|
|
17
|
+
ROLES_REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(
|
|
18
|
+
os.path.abspath(__file__))))) # repo root (4 up from tests/)
|
|
19
|
+
# Bound-skill resolution requires the Aero Agent Skills checkout. It is a
|
|
20
|
+
# cross-repo dev check: when the skills repo is absent (fresh public clone),
|
|
21
|
+
# skip resolution rather than fail - the role's bound list is verified in CI.
|
|
22
|
+
AEROSKILLS = os.environ.get("AEROSKILLS_DEV", os.path.expanduser("~/AeroSkills"))
|
|
23
|
+
HAS_SKILLS = os.path.isdir(os.path.join(AEROSKILLS, "skills"))
|
|
24
|
+
ROLE_DIR = os.path.join(ROLES_REPO, "roles", "do178c-cert-engineer")
|
|
25
|
+
PSAC_TEMPLATE = os.path.join(ROLE_DIR, "templates", "psac-template.md")
|
|
26
|
+
|
|
27
|
+
EXPECTED_BOUND = [
|
|
28
|
+
"avionics/do178c/planning",
|
|
29
|
+
"avionics/do178c/development",
|
|
30
|
+
"avionics/do178c/verification",
|
|
31
|
+
"avionics/do178c/software-testing",
|
|
32
|
+
"avionics/do178c/configuration-management",
|
|
33
|
+
"avionics/do178c/tool-qualification",
|
|
34
|
+
"avionics/do178c/previously-developed-software",
|
|
35
|
+
"avionics/do178c/data-control-coupling-analysis",
|
|
36
|
+
"avionics/do178c/airworthiness-liaison",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
EXPECTED_STAGES = [
|
|
40
|
+
"Planning", "Dev framework", "Verification strategy", "Test evidence",
|
|
41
|
+
"CM", "Tool qualification", "PDS check", "Coupling analysis",
|
|
42
|
+
"Airworthiness liaison",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def read_role_md():
|
|
47
|
+
p = os.path.join(ROLE_DIR, "ROLE.md")
|
|
48
|
+
if not os.path.exists(p):
|
|
49
|
+
return None
|
|
50
|
+
return open(p).read()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class TestDo178cRole(unittest.TestCase):
|
|
54
|
+
|
|
55
|
+
def test_bound_skills_resolve_in_aeroskills(self):
|
|
56
|
+
if not HAS_SKILLS:
|
|
57
|
+
self.skipTest("Aero Agent Skills checkout not present (cross-repo dev check)")
|
|
58
|
+
for leaf in EXPECTED_BOUND:
|
|
59
|
+
sk = os.path.join(AEROSKILLS, "skills", leaf, "SKILL.md")
|
|
60
|
+
self.assertTrue(
|
|
61
|
+
os.path.exists(sk),
|
|
62
|
+
f"bound skill not found in aero-agent-skills: {leaf}")
|
|
63
|
+
|
|
64
|
+
def test_bound_skills_listed_in_frontmatter(self):
|
|
65
|
+
role = read_role_md()
|
|
66
|
+
self.assertIsNotNone(role)
|
|
67
|
+
for leaf in EXPECTED_BOUND:
|
|
68
|
+
self.assertIn(leaf, role, f"missing in ROLE.md skills_bound: {leaf}")
|
|
69
|
+
|
|
70
|
+
def test_workflow_stages_deterministic(self):
|
|
71
|
+
role = read_role_md()
|
|
72
|
+
# every expected stage appears in order (indexes ascending)
|
|
73
|
+
idx = [role.find(stage) for stage in EXPECTED_STAGES]
|
|
74
|
+
self.assertTrue(all(i >= 0 for i in idx), "a stage is missing")
|
|
75
|
+
self.assertEqual(idx, sorted(idx), "stage order not deterministic")
|
|
76
|
+
|
|
77
|
+
def test_deliverable_template_complete(self):
|
|
78
|
+
if not os.path.exists(PSAC_TEMPLATE):
|
|
79
|
+
self.fail("psac-template.md missing")
|
|
80
|
+
text = open(PSAC_TEMPLATE).read()
|
|
81
|
+
# all 9 numbered sections present
|
|
82
|
+
for n in range(1, 10):
|
|
83
|
+
self.assertTrue(
|
|
84
|
+
re.search(rf"^## {n}\. ", text, re.M),
|
|
85
|
+
f"section {n} missing")
|
|
86
|
+
self.assertIn("DRAFT", text)
|
|
87
|
+
low = text.lower()
|
|
88
|
+
self.assertIn("not an approval", low)
|
|
89
|
+
self.assertIn("document", low)
|
|
90
|
+
|
|
91
|
+
def test_forbidden_lines_present(self):
|
|
92
|
+
role = read_role_md()
|
|
93
|
+
for phrase in ["issue certification approval", "regulatory sign-off",
|
|
94
|
+
"reproduce proprietary", "sign_off_required: true"]:
|
|
95
|
+
self.assertIn(phrase, role, f"missing boundary: {phrase}")
|
|
96
|
+
|
|
97
|
+
def test_sources_register_exists(self):
|
|
98
|
+
p = os.path.join(ROLE_DIR, "SOURCES.md")
|
|
99
|
+
self.assertTrue(os.path.exists(p), "SOURCES.md missing for role")
|
|
100
|
+
|
|
101
|
+
def test_core_engine_exists(self):
|
|
102
|
+
"""100% standard: every role ships an executable core + cli."""
|
|
103
|
+
for f in ["core", "cli.py"]:
|
|
104
|
+
self.assertTrue(os.path.exists(os.path.join(ROLE_DIR, f)),
|
|
105
|
+
f"{f} missing - role is not executable")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
unittest.main()
|