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,99 @@
|
|
|
1
|
+
# Concept Design Package - RegionalJet-1500 (100 pax / 1500 nm / M0.78)
|
|
2
|
+
|
|
3
|
+
**Document type:** Concept Design Package
|
|
4
|
+
**Status:** draft-for-review
|
|
5
|
+
**Aircraft class:** transport (class-I conceptual sizing)
|
|
6
|
+
**Generated:** 2026-09-05
|
|
7
|
+
|
|
8
|
+
## 1. Requirements and design space
|
|
9
|
+
|
|
10
|
+
| Requirement | Value |
|
|
11
|
+
|---|---|
|
|
12
|
+
| Passengers | 100 pax |
|
|
13
|
+
| Payload | 20,000 lb (100 pax x 200 lb + baggage, class-I) |
|
|
14
|
+
| Design range | 1,500 nm |
|
|
15
|
+
| Cruise | M0.78 at 10,668 m (FL350), 450 kt TAS |
|
|
16
|
+
| Takeoff field length | 1,829 m (6,001 ft) |
|
|
17
|
+
| Engines | 2 turbofans |
|
|
18
|
+
|
|
19
|
+
Constraint diagram (matching chart, T/W vs W/S; SI inputs, sea-level ISA for takeoff, cruise at FL350):
|
|
20
|
+
|
|
21
|
+
| Constraint | Rule | Value at design W/S |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| Stall (max W/S) | W/S = 0.5*rho*CLmax*VS^2 | 5,512 N/m^2 (115.1 psf) |
|
|
24
|
+
| Takeoff | T/W = 1.21*(W/S)/(rho*g*CLmax*s_TO) | 0.1404 |
|
|
25
|
+
| Climb | T/W = 1/LD + gamma (OEI -> installed, x n/(n-1)) | 0.2147 |
|
|
26
|
+
| Cruise | T/W = q*CD0/(W/S) + k*(W/S)/q | 0.0569 |
|
|
27
|
+
|
|
28
|
+
**Selected design point:** W/S = 5,100 N/m^2 (106.5 psf), T/W = 0.2147 (binding constraint: climb).
|
|
29
|
+
**Feasible:** True - the point lies on the lower bound of the feasible region (stall limit not exceeded).
|
|
30
|
+
Feasible-region boundary samples (W/S N/m^2 -> minimum T/W): 1,378 -> 0.2147, 1,895 -> 0.2147, 2,412 -> 0.2147, 2,929 -> 0.2147, 3,445 -> 0.2147, 3,962 -> 0.2147, 4,479 -> 0.2147, 4,996 -> 0.2147, 5,512 -> 0.2147.
|
|
31
|
+
|
|
32
|
+
## 2. Sizing mission
|
|
33
|
+
|
|
34
|
+
Mission profile (segments in flight order; each burns from the weight remaining after earlier segments, Breguet range/endurance models):
|
|
35
|
+
|
|
36
|
+
| Segment | Fuel burned | Fraction of MTOW |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| Taxi | 300 lb | 0.2% of MTOW |
|
|
39
|
+
| Takeoff | 900 lb | 0.7% of MTOW |
|
|
40
|
+
| Climb | 6,000 lb | 5.0% of MTOW |
|
|
41
|
+
| Cruise (1,500 nm @ 450 kt) | 30,057 lb | 25.0% of MTOW |
|
|
42
|
+
| Descent | 750 lb | 0.6% of MTOW |
|
|
43
|
+
|
|
44
|
+
| **Block fuel** | **38,007 lb** | **31.6%** |
|
|
45
|
+
| Block time | 4.19 hr | |
|
|
46
|
+
| Landing weight | 82,292 lb | |
|
|
47
|
+
| Reserve (hold45_5pct: 45 min hold at 1500 ft + 5% trip) | 3,932 lb | |
|
|
48
|
+
| **Required fuel (mission + reserves)** | **41,939 lb** | **34.9% of MTOW** |
|
|
49
|
+
|
|
50
|
+
## 3. Configuration (from the design point)
|
|
51
|
+
|
|
52
|
+
- Wing loading W/S: 5,100 N/m^2 (106.5 psf) - design choice, 92.5% of the stall-limited maximum.
|
|
53
|
+
- Wing area S = W/(W/S) = 104.9 m^2 (1,129 ft^2); aspect ratio 9.5; span b = 31.6 m.
|
|
54
|
+
- Drag model: CD0 = 0.018, e = 0.80, k = 1/(pi*e*AR) = 0.0419; L/D_max = 18.2 (consistent with the mission cruise L/D of 18).
|
|
55
|
+
- Engines: 2 x ~12,912 lbf class sea-level static thrust (see Section 5).
|
|
56
|
+
- 3-view reference geometry from the OpenVSP stage (initial layout) feeds the class-II mass build-up; not required for the class-I sizing closure.
|
|
57
|
+
|
|
58
|
+
## 4. Mass statement (class-I, balances to converged MTOW)
|
|
59
|
+
|
|
60
|
+
| Group | Weight (lb) | % MTOW |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| Operating empty weight (class-I fit 0.485 x MTOW) | 58,345 | 48.5% |
|
|
63
|
+
| Design payload (100 pax x 200 lb + cargo) | 20,000 | 16.6% |
|
|
64
|
+
| Fuel (mission + reserves, hold45_5pct) | 41,939 | 34.9% |
|
|
65
|
+
| **Maximum takeoff weight (converged)** | **120,298** | **100.0%** |
|
|
66
|
+
|
|
67
|
+
- Balance check (empty + payload + fuel vs MTOW): PASS within 1%.
|
|
68
|
+
- Empty-weight fraction 0.485 inside the transport class-I band 0.42-0.55: PASS.
|
|
69
|
+
- Weight-growth context: conceptual phase growth allowance 10% applies to the detailed mass budget roll-up (mass-budget stage).
|
|
70
|
+
- CG envelope and critical loading conditions require the 3-view layout and the mass-properties stage (component arms); the class-I closure above bounds the balance (fuel + payload = 61,939 lb available below MTOW - OEW = 61,954 lb).
|
|
71
|
+
|
|
72
|
+
## 5. MTOW convergence and engine size check
|
|
73
|
+
|
|
74
|
+
Fuel-fraction sizing iteration (W0 = payload / (1 - W_e/W0 - W_f/W0), class-I empty fraction 0.485):
|
|
75
|
+
|
|
76
|
+
| Iteration | Guess (lb) | Fuel fraction | New MTOW (lb) | Rel. change |
|
|
77
|
+
|---|---|---|---|---|
|
|
78
|
+
| 1 | 100,000 | 0.3591 | 128,259 | 28.26% |
|
|
79
|
+
| 2 | 128,259 | 0.3454 | 117,944 | 8.04% |
|
|
80
|
+
| 3 | 117,944 | 0.3496 | 120,955 | 2.55% |
|
|
81
|
+
| 4 | 120,955 | 0.3483 | 120,007 | 0.78% |
|
|
82
|
+
| 5 | 120,007 | 0.3487 | 120,298 | 0.24% |
|
|
83
|
+
|
|
84
|
+
**Converged MTOW = 120,298 lb (54,566 kg)** after 5 iterations (tolerance 0.5%): CONVERGED.
|
|
85
|
+
|
|
86
|
+
- Installed sea-level thrust: 25,824 lbf total = 12,912 lbf per engine (T/W = 0.215).
|
|
87
|
+
- Engine weight estimate: ~2,582 lb per engine at engine T/W = 5.0 (4-6 typical band).
|
|
88
|
+
- Top-of-climb margin at 10,668 m (thrust lapse sigma^0.7 vs W/(L/D)): 1.81 - PASS (>= 1.0).
|
|
89
|
+
- Engine-out second-segment gradient is carried on the installed basis: T/W = (n/(n-1)) x (1/LD + gamma) with LD = 12 and gamma = 0.024 (FAR 25.121(b)(1) two-engine value, 2 engines); the engine-sizing stage re-checks OEI climb, hot day, and takeoff derate against the catalogue engine.
|
|
90
|
+
|
|
91
|
+
## 6. Feasibility notes and next stages
|
|
92
|
+
|
|
93
|
+
- Class-I results are FEASIBILITY, not detailed design: the converged MTOW, the W/S-T/W design point, and the mass statement close the sizing loop within the stated class-I model accuracy.
|
|
94
|
+
- Structure/propulsion/systems mass split requires the class-II component build-up (weight-estimation, engine-sizing, fuselage/wing/tail-sizing leaves); the conceptual growth allowance (10%) is the margin policy for that roll-up.
|
|
95
|
+
- Payload-range, cost (parametric DOC/LCC), and CG envelope are produced by the dedicated stages (payload-range-diagram, parametric-cost, cg-envelope) once the class-II breakdown exists.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
*DRAFT - conceptual feasibility for human design review. Not a production release and not an approval document. Conceptual results are class-I estimates; every configuration choice must trace to the trade data before detailed design.*
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Test aircraft_design_core: the executable engine of the aircraft
|
|
3
|
+
conceptual design engineer role.
|
|
4
|
+
|
|
5
|
+
Proves the role can DO its job standalone (no AeroSkills needed):
|
|
6
|
+
constraint-analysis T/W rules, mission fuel-fraction models, class-I
|
|
7
|
+
empty-weight fit, MTOW convergence within 0.5%, concept package
|
|
8
|
+
generation, and evidence-gate checks.
|
|
9
|
+
"""
|
|
10
|
+
import math
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
import unittest
|
|
14
|
+
|
|
15
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "core"))
|
|
16
|
+
from aircraft_design_core import ( # noqa: E402
|
|
17
|
+
EMPTY_WEIGHT_FRACTION_BANDS, breguet_cruise_fuel_fraction,
|
|
18
|
+
breguet_loiter_fuel_fraction, build_concept, chain_mission, check_concept,
|
|
19
|
+
check_concept_markdown, check_empty_weight_fraction, climb_constraint_tw,
|
|
20
|
+
constraint_tw, cruise_constraint_tw, empty_weight_fraction,
|
|
21
|
+
example_item, example_concept_markdown, render_concept_markdown,
|
|
22
|
+
segment_fuel_fraction, size_mtow, stall_max_ws, takeoff_constraint_tw,
|
|
23
|
+
tow_estimate, weight_breakdown_ok,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
G = 9.80665
|
|
27
|
+
RHO_SL = 1.225
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TestConstraintAnalysis(unittest.TestCase):
|
|
31
|
+
"""Matching-chart rules must match the AeroSkills leaf formulas."""
|
|
32
|
+
|
|
33
|
+
def test_stall_max_ws(self):
|
|
34
|
+
# W/S = 0.5*rho*CLmax*VS^2: 0.5*1.225*2.5*60^2
|
|
35
|
+
self.assertAlmostEqual(stall_max_ws(60.0, 2.5), 0.5 * 1.225 * 2.5 * 3600.0)
|
|
36
|
+
with self.assertRaises(ValueError):
|
|
37
|
+
stall_max_ws(-1.0, 2.5)
|
|
38
|
+
|
|
39
|
+
def test_takeoff_constraint(self):
|
|
40
|
+
# T/W = 1.21*(W/S)/(rho*g*CLmax*s_TO) with clean SI inputs
|
|
41
|
+
ws, rho, cl, s_to = 4000.0, 1.225, 2.0, 1500.0
|
|
42
|
+
expected = 1.21 * ws / (rho * G * cl * s_to)
|
|
43
|
+
self.assertAlmostEqual(takeoff_constraint_tw(ws, rho, cl, s_to), expected)
|
|
44
|
+
# dispatcher agrees with the direct leaf call
|
|
45
|
+
self.assertAlmostEqual(
|
|
46
|
+
constraint_tw("takeoff", W_S=ws, rho=rho, CLmax=cl, s_TO=s_to),
|
|
47
|
+
expected)
|
|
48
|
+
with self.assertRaises(ValueError):
|
|
49
|
+
takeoff_constraint_tw(-5.0, rho, cl, s_to)
|
|
50
|
+
|
|
51
|
+
def test_climb_constraint(self):
|
|
52
|
+
# T/W = 1/LD + gamma
|
|
53
|
+
self.assertAlmostEqual(climb_constraint_tw(20.0, 0.03), 1.0 / 20.0 + 0.03)
|
|
54
|
+
# OEI second-segment on the installed basis: x n/(n-1)
|
|
55
|
+
raw = constraint_tw("climb", LD=12.0, gamma=0.024)
|
|
56
|
+
inst = constraint_tw("climb", LD=12.0, gamma=0.024, engines=2)
|
|
57
|
+
self.assertAlmostEqual(raw, 1.0 / 12.0 + 0.024)
|
|
58
|
+
self.assertAlmostEqual(inst, 2.0 * raw)
|
|
59
|
+
with self.assertRaises(ValueError):
|
|
60
|
+
constraint_tw("climb", LD=12.0, gamma=0.024, engines=1)
|
|
61
|
+
|
|
62
|
+
def test_cruise_constraint_shape(self):
|
|
63
|
+
# q*CD0/W_S + k*W_S/q is U-shaped in W/S with its minimum at
|
|
64
|
+
# W/S* = q*sqrt(CD0/k); takeoff grows with W/S, so high W/S values
|
|
65
|
+
# are takeoff-bound while very low W/S values are cruise-bound.
|
|
66
|
+
v, rho, cd0, k = 231.3, 0.3796, 0.018, 0.0419
|
|
67
|
+
q = 0.5 * rho * v * v
|
|
68
|
+
ws_opt = q * math.sqrt(cd0 / k)
|
|
69
|
+
vals = [cruise_constraint_tw(w, v, rho, cd0, k)
|
|
70
|
+
for w in (ws_opt * 0.7, ws_opt, ws_opt * 1.3)]
|
|
71
|
+
self.assertLess(vals[1], vals[0])
|
|
72
|
+
self.assertLess(vals[1], vals[2])
|
|
73
|
+
with self.assertRaises(ValueError):
|
|
74
|
+
cruise_constraint_tw(-1.0, v, rho, cd0, k)
|
|
75
|
+
with self.assertRaises(ValueError):
|
|
76
|
+
constraint_tw("bogus", W_S=4000.0)
|
|
77
|
+
|
|
78
|
+
def test_design_point_on_feasible_boundary(self):
|
|
79
|
+
item = example_item()
|
|
80
|
+
model = build_concept(item)
|
|
81
|
+
dp = model["design_point"]
|
|
82
|
+
# chosen W/S below the stall limit and inside the feasible region
|
|
83
|
+
self.assertLessEqual(dp["ws_nm2"], dp["ws_stall_max_nm2"])
|
|
84
|
+
self.assertTrue(dp["feasible"])
|
|
85
|
+
# required T/W equals the max over the constraint curves at that W/S
|
|
86
|
+
max_curve = max(dp["curves"][name]["tw_at_design"]
|
|
87
|
+
for name in dp["curves"])
|
|
88
|
+
self.assertAlmostEqual(dp["tw_required"], max_curve, places=6)
|
|
89
|
+
self.assertIn(dp["binding_constraint"], dp["curves"])
|
|
90
|
+
# the example has a real, defensible regional-jet design point
|
|
91
|
+
self.assertGreater(dp["tw_required"], 0.10)
|
|
92
|
+
self.assertLess(dp["tw_required"], 0.35)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class TestMissionFuelFractions(unittest.TestCase):
|
|
96
|
+
"""Segment fuel models must match the sizing-mission-profile leaf."""
|
|
97
|
+
|
|
98
|
+
def test_breguet_cruise_fraction_matches_doc_case(self):
|
|
99
|
+
# AeroSkills worked example: 3000 nm, V 450 kt, TSFC 0.6, L/D 18,
|
|
100
|
+
# W0 150000 lb burns 69088.9 lb on the cruise leg -> fraction 0.46059
|
|
101
|
+
frac = breguet_cruise_fuel_fraction(3000.0, 450.0, 0.6, 18.0)
|
|
102
|
+
self.assertAlmostEqual(frac * 150000.0, 69088.9, delta=1.0)
|
|
103
|
+
|
|
104
|
+
def test_loiter_endurance_fraction(self):
|
|
105
|
+
# hold 0.75 hr at TSFC 0.5, L/D 15: 1 - exp(-0.75*0.5/15)
|
|
106
|
+
frac = breguet_loiter_fuel_fraction(0.75, 0.5, 15.0)
|
|
107
|
+
self.assertAlmostEqual(frac, 1.0 - math.exp(-0.75 * 0.5 / 15.0))
|
|
108
|
+
|
|
109
|
+
def test_segment_fuel_fraction_dispatch(self):
|
|
110
|
+
# absolute-flow segments scale with the start weight (W0-dependent)
|
|
111
|
+
f1 = segment_fuel_fraction("taxi", {"fuel_flow": 1200.0, "time": 0.25},
|
|
112
|
+
120000.0)
|
|
113
|
+
self.assertAlmostEqual(f1, 300.0 / 120000.0)
|
|
114
|
+
# climb fraction model returns the fraction directly
|
|
115
|
+
self.assertAlmostEqual(
|
|
116
|
+
segment_fuel_fraction("climb", {"fraction": 0.01}, 120000.0), 0.01)
|
|
117
|
+
# cruise dispatches to Breguet range
|
|
118
|
+
fc = segment_fuel_fraction("cruise", {"R_nm": 1500.0, "V_kt": 450.0,
|
|
119
|
+
"TSFC": 0.6, "LD": 18.0},
|
|
120
|
+
120000.0)
|
|
121
|
+
self.assertAlmostEqual(fc, 1.0 - math.exp(-1500.0 / (450.0 * 0.6 * 18.0)))
|
|
122
|
+
with self.assertRaises(ValueError):
|
|
123
|
+
segment_fuel_fraction("weird", {}, 120000.0)
|
|
124
|
+
|
|
125
|
+
def test_mission_chains_weights_and_balances_fractions(self):
|
|
126
|
+
item = example_item()
|
|
127
|
+
mission = chain_mission(item, 150000.0)
|
|
128
|
+
# weight chains: block fuel < start weight, landing weight positive
|
|
129
|
+
self.assertGreater(mission["block_fuel_lb"], 0.0)
|
|
130
|
+
self.assertGreater(mission["landing_weight_lb"], 0.0)
|
|
131
|
+
# required fuel = block + reserve; reserve = hold + 5% of trip
|
|
132
|
+
self.assertAlmostEqual(mission["required_fuel_lb"],
|
|
133
|
+
mission["block_fuel_lb"] + mission["reserve_fuel_lb"])
|
|
134
|
+
# per-segment fractions of MTOW sum to the block-fuel fraction
|
|
135
|
+
total_frac = sum(s["fraction_of_mtow"] for s in mission["segments"])
|
|
136
|
+
self.assertAlmostEqual(total_frac,
|
|
137
|
+
mission["block_fuel_lb"] / 150000.0)
|
|
138
|
+
# segment types in the expected order
|
|
139
|
+
types = [s["type"] for s in mission["segments"]]
|
|
140
|
+
self.assertEqual(types, ["taxi", "takeoff", "climb", "cruise", "descent"])
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class TestEmptyFractionAndTow(unittest.TestCase):
|
|
144
|
+
"""Class-I empty-weight fit and the tow-estimation leaf parity."""
|
|
145
|
+
|
|
146
|
+
def test_empty_weight_fraction_band_and_fit(self):
|
|
147
|
+
self.assertEqual(EMPTY_WEIGHT_FRACTION_BANDS["transport"], (0.42, 0.55))
|
|
148
|
+
# representative fraction is the transport band midpoint
|
|
149
|
+
self.assertAlmostEqual(empty_weight_fraction("transport"), 0.485)
|
|
150
|
+
with self.assertRaises(ValueError):
|
|
151
|
+
empty_weight_fraction("airship")
|
|
152
|
+
|
|
153
|
+
def test_empty_fraction_in_band_check(self):
|
|
154
|
+
in_band, band, frac = check_empty_weight_fraction(58300.0, 120000.0,
|
|
155
|
+
"transport")
|
|
156
|
+
self.assertTrue(in_band)
|
|
157
|
+
self.assertEqual(band, (0.42, 0.55))
|
|
158
|
+
self.assertAlmostEqual(frac, 58300.0 / 120000.0)
|
|
159
|
+
# out-of-band case fails
|
|
160
|
+
in_band2, _, _ = check_empty_weight_fraction(80000.0, 120000.0, "transport")
|
|
161
|
+
self.assertFalse(in_band2)
|
|
162
|
+
|
|
163
|
+
def test_tow_estimate_leaf(self):
|
|
164
|
+
# W0 = payload/(1 - empty - fuel)
|
|
165
|
+
self.assertAlmostEqual(tow_estimate(20000.0, 0.485, 0.35),
|
|
166
|
+
20000.0 / (1.0 - 0.485 - 0.35))
|
|
167
|
+
with self.assertRaises(ValueError):
|
|
168
|
+
tow_estimate(20000.0, 0.6, 0.5) # fractions reach 1.0
|
|
169
|
+
|
|
170
|
+
def test_weight_breakdown_balances(self):
|
|
171
|
+
self.assertTrue(weight_breakdown_ok(58000.0, 42000.0, 20000.0, 120000.0))
|
|
172
|
+
self.assertFalse(weight_breakdown_ok(60000.0, 42000.0, 20000.0, 120000.0))
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class TestMTOWConvergence(unittest.TestCase):
|
|
176
|
+
"""The sizing loop must genuinely iterate and converge within 0.5%."""
|
|
177
|
+
|
|
178
|
+
def test_mtow_converges_within_tolerance(self):
|
|
179
|
+
item = example_item()
|
|
180
|
+
sizing = size_mtow(item)
|
|
181
|
+
self.assertTrue(sizing["converged"])
|
|
182
|
+
self.assertEqual(sizing["tolerance"], 0.005)
|
|
183
|
+
self.assertGreaterEqual(len(sizing["iterations"]), 2)
|
|
184
|
+
# final successive estimates within the 0.5% band
|
|
185
|
+
last = sizing["iterations"][-1]
|
|
186
|
+
self.assertLess(last["rel_change"], 0.005)
|
|
187
|
+
# MTOW dominates payload and matches a 100-pax regional jet class
|
|
188
|
+
self.assertGreater(sizing["mtow_lb"], 3.0 * sizing["payload_lb"])
|
|
189
|
+
self.assertGreater(sizing["mtow_lb"], 90000.0)
|
|
190
|
+
self.assertLess(sizing["mtow_lb"], 160000.0)
|
|
191
|
+
|
|
192
|
+
def test_fuel_fraction_is_weight_dependent(self):
|
|
193
|
+
# fixed-fuel segments make the fuel fraction fall as W0 grows
|
|
194
|
+
f_small = chain_mission(example_item(), 80000.0)["fuel_fraction"]
|
|
195
|
+
f_large = chain_mission(example_item(), 160000.0)["fuel_fraction"]
|
|
196
|
+
self.assertLess(f_large, f_small)
|
|
197
|
+
|
|
198
|
+
def test_breakdown_balances_at_convergence(self):
|
|
199
|
+
sizing = size_mtow(example_item())
|
|
200
|
+
self.assertTrue(weight_breakdown_ok(
|
|
201
|
+
sizing["empty_weight_lb"], sizing["required_fuel_lb"],
|
|
202
|
+
sizing["payload_lb"], sizing["mtow_lb"], tol=0.01))
|
|
203
|
+
|
|
204
|
+
def test_converges_from_a_different_guess(self):
|
|
205
|
+
item = example_item()
|
|
206
|
+
item.initial_guess_lb = 250000.0
|
|
207
|
+
sizing = size_mtow(item)
|
|
208
|
+
self.assertTrue(sizing["converged"])
|
|
209
|
+
# converges to the same fixed point regardless of the start guess
|
|
210
|
+
base = size_mtow(example_item())["mtow_lb"]
|
|
211
|
+
self.assertAlmostEqual(sizing["mtow_lb"], base, delta=base * 0.005)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class TestConceptPackageAndGates(unittest.TestCase):
|
|
215
|
+
|
|
216
|
+
def test_build_concept_model_complete(self):
|
|
217
|
+
model = build_concept(example_item())
|
|
218
|
+
for key in ("document_type", "status", "item", "requirement",
|
|
219
|
+
"design_point", "mission", "sizing", "engine",
|
|
220
|
+
"mass_statement"):
|
|
221
|
+
self.assertIn(key, model)
|
|
222
|
+
ms = model["mass_statement"]
|
|
223
|
+
self.assertEqual(model["status"], "draft-for-review")
|
|
224
|
+
self.assertTrue(model["sizing"]["empty_in_band"])
|
|
225
|
+
# mass statement carries the three rows and a total
|
|
226
|
+
self.assertEqual(len(ms["rows"]), 3)
|
|
227
|
+
self.assertAlmostEqual(ms["total_lb"], model["sizing"]["mtow_lb"])
|
|
228
|
+
# engine check: 2 engines, positive thrust, top-of-climb margin >= 1
|
|
229
|
+
self.assertEqual(model["engine"]["n_engines"], 2)
|
|
230
|
+
self.assertGreater(model["engine"]["toc_margin"], 1.0)
|
|
231
|
+
self.assertTrue(model["engine"]["toc_ok"])
|
|
232
|
+
|
|
233
|
+
def test_core_gates_all_pass(self):
|
|
234
|
+
model = build_concept(example_item())
|
|
235
|
+
gates = check_concept(model)
|
|
236
|
+
self.assertTrue(gates["all_pass"], gates)
|
|
237
|
+
|
|
238
|
+
def test_markdown_gates_all_pass_and_no_blanks(self):
|
|
239
|
+
md = example_concept_markdown()
|
|
240
|
+
self.assertGreater(len(md), 3000)
|
|
241
|
+
self.assertNotIn("___", md)
|
|
242
|
+
gates = check_concept_markdown(md)
|
|
243
|
+
self.assertTrue(gates["all_pass"], gates)
|
|
244
|
+
|
|
245
|
+
def test_render_contains_key_numbers(self):
|
|
246
|
+
md = example_concept_markdown()
|
|
247
|
+
model = build_concept(example_item())
|
|
248
|
+
s = model["sizing"]
|
|
249
|
+
dp = model["design_point"]
|
|
250
|
+
# converged MTOW, W/S-T/W point, and the mass statement are present
|
|
251
|
+
self.assertIn(f"{s['mtow_lb']:,.0f}", md)
|
|
252
|
+
self.assertIn(f"{dp['ws_nm2']:,.0f} N/m^2", md)
|
|
253
|
+
self.assertIn(f"{dp['tw_required']:.4f}", md)
|
|
254
|
+
self.assertIn("CONVERGED", md.upper())
|
|
255
|
+
self.assertIn("not an approval", md.lower())
|
|
256
|
+
|
|
257
|
+
def test_standalone_no_skills_repo(self):
|
|
258
|
+
# Core must work with no AeroSkills present and no local paths.
|
|
259
|
+
os.environ["AEROSKILLS_DEV"] = "/nonexistent"
|
|
260
|
+
model = build_concept(example_item())
|
|
261
|
+
md = render_concept_markdown(model)
|
|
262
|
+
self.assertTrue(model["sizing"]["converged"])
|
|
263
|
+
self.assertGreater(len(md), 3000)
|
|
264
|
+
gates = check_concept(model)
|
|
265
|
+
self.assertTrue(gates["all_pass"], gates)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
if __name__ == "__main__":
|
|
269
|
+
unittest.main()
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Role test: aircraft-design-engineer."""
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
ROLES_REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(
|
|
8
|
+
os.path.abspath(__file__)))))
|
|
9
|
+
AEROSKILLS = os.environ.get("AEROSKILLS_DEV", os.path.expanduser("~/AeroSkills"))
|
|
10
|
+
HAS_SKILLS = os.path.isdir(os.path.join(AEROSKILLS, "skills"))
|
|
11
|
+
ROLE_DIR = os.path.join(ROLES_REPO, "roles", "aircraft-design-engineer")
|
|
12
|
+
TEMPLATE = os.path.join(ROLE_DIR, "templates", "concept-design-template.md")
|
|
13
|
+
|
|
14
|
+
EXPECTED_BOUND = [
|
|
15
|
+
"vehicle-design/conceptual/constraint-analysis",
|
|
16
|
+
"vehicle-design/conceptual/sizing-mission-profile",
|
|
17
|
+
"vehicle-design/conceptual/payload-range-diagram",
|
|
18
|
+
"vehicle-design/conceptual/tow-estimation",
|
|
19
|
+
"vehicle-design/conceptual/openvsp-geometry",
|
|
20
|
+
"vehicle-design/sizing/ws-tw-trade",
|
|
21
|
+
"vehicle-design/sizing/weight-estimation",
|
|
22
|
+
"vehicle-design/sizing/engine-sizing",
|
|
23
|
+
"vehicle-design/sizing/fuselage-sizing",
|
|
24
|
+
"vehicle-design/sizing/wing-planform-sizing",
|
|
25
|
+
"vehicle-design/sizing/tail-sizing",
|
|
26
|
+
"vehicle-design/sizing/landing-gear-sizing",
|
|
27
|
+
"vehicle-design/sizing/control-surface-sizing",
|
|
28
|
+
"vehicle-design/sizing/fuel-tank-sizing",
|
|
29
|
+
"vehicle-design/sizing/battery-sizing",
|
|
30
|
+
"vehicle-design/sizing/environmental-control-sizing",
|
|
31
|
+
"vehicle-design/sizing/electrical-wire-sizing",
|
|
32
|
+
"vehicle-design/sizing/apu-fuel-burn-sizing",
|
|
33
|
+
"vehicle-design/sizing/bleed-air-system-sizing",
|
|
34
|
+
"vehicle-design/sizing/fire-protection-sizing",
|
|
35
|
+
"vehicle-design/sizing/ice-protection-sizing",
|
|
36
|
+
"vehicle-design/sizing/hydraulic-system-sizing",
|
|
37
|
+
"vehicle-design/sizing/landing-gear-retraction-sizing",
|
|
38
|
+
"vehicle-design/mass-properties/mass-budget",
|
|
39
|
+
"vehicle-design/mass-properties/cg-envelope",
|
|
40
|
+
"vehicle-design/mass-properties/inertia-estimation",
|
|
41
|
+
"vehicle-design/mdo/design-of-experiments",
|
|
42
|
+
"vehicle-design/mdo/multidisciplinary-optimization",
|
|
43
|
+
"vehicle-design/mdo/surrogate-modeling",
|
|
44
|
+
"vehicle-design/cost-estimation/parametric-cost",
|
|
45
|
+
"vehicle-design/cost-estimation/operating-cost",
|
|
46
|
+
"vehicle-design/cost-estimation/life-cycle-cost",
|
|
47
|
+
"vehicle-design/structures-integration/wing-box-sizing",
|
|
48
|
+
"vehicle-design/structures-integration/fuselage-skin-stringer"
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
STAGES = [
|
|
52
|
+
"1. Requirements framing",
|
|
53
|
+
"2. Sizing mission",
|
|
54
|
+
"3. Initial layout",
|
|
55
|
+
"4. W/S vs T/W trade",
|
|
56
|
+
"5. Weight iteration",
|
|
57
|
+
"6. Component sizing",
|
|
58
|
+
"7. Subsystem sizing",
|
|
59
|
+
"8. Mass + CG",
|
|
60
|
+
"9. MDO exploration",
|
|
61
|
+
"10. Payload-range",
|
|
62
|
+
"11. Cost",
|
|
63
|
+
"12. Structures check",
|
|
64
|
+
"13. Package"
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def role_text():
|
|
69
|
+
p = os.path.join(ROLE_DIR, "ROLE.md")
|
|
70
|
+
return open(p).read() if os.path.exists(p) else None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class TestAircraftDesignEngineerRole(unittest.TestCase):
|
|
74
|
+
|
|
75
|
+
def test_bound_skills_resolve(self):
|
|
76
|
+
if not HAS_SKILLS:
|
|
77
|
+
self.skipTest("skills checkout absent (cross-repo dev check)")
|
|
78
|
+
for leaf in EXPECTED_BOUND:
|
|
79
|
+
self.assertTrue(
|
|
80
|
+
os.path.exists(os.path.join(AEROSKILLS, "skills", leaf, "SKILL.md")),
|
|
81
|
+
f"bound skill missing: {leaf}")
|
|
82
|
+
|
|
83
|
+
def test_stages_ordered(self):
|
|
84
|
+
role = role_text()
|
|
85
|
+
self.assertIsNotNone(role)
|
|
86
|
+
wf = role[role.find("## Workflow"):role.find("## Evidence gates")]
|
|
87
|
+
idx = [wf.find(s) for s in STAGES]
|
|
88
|
+
self.assertTrue(all(i >= 0 for i in idx), "stage missing in workflow")
|
|
89
|
+
self.assertEqual(idx, sorted(idx), "stage order not deterministic")
|
|
90
|
+
|
|
91
|
+
def test_template_present(self):
|
|
92
|
+
self.assertTrue(os.path.exists(TEMPLATE), "deliverable template missing")
|
|
93
|
+
|
|
94
|
+
def test_boundaries(self):
|
|
95
|
+
role = role_text()
|
|
96
|
+
self.assertIsNotNone(role)
|
|
97
|
+
for phrase in ["sign_off_required: true", "forbidden"]:
|
|
98
|
+
self.assertIn(phrase, role)
|
|
99
|
+
|
|
100
|
+
def test_sources(self):
|
|
101
|
+
self.assertTrue(os.path.exists(os.path.join(ROLE_DIR, "SOURCES.md")),
|
|
102
|
+
"SOURCES.md missing")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
if __name__ == "__main__":
|
|
106
|
+
unittest.main()
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: role
|
|
3
|
+
name: airworthiness-compliance-engineer
|
|
4
|
+
title: "Airworthiness Compliance Engineer"
|
|
5
|
+
status: draft
|
|
6
|
+
domain: systems-engineering-safety
|
|
7
|
+
deliverable_type: "compliance checklist / certification matrix"
|
|
8
|
+
standards_bound:
|
|
9
|
+
- id: far-25
|
|
10
|
+
tier: TIER-1
|
|
11
|
+
reference-only: false
|
|
12
|
+
- id: cs-25
|
|
13
|
+
tier: TIER-1
|
|
14
|
+
reference-only: false
|
|
15
|
+
skills_bound:
|
|
16
|
+
- avionics/far-cs25/airworthiness
|
|
17
|
+
- avionics/far-cs25/special-conditions
|
|
18
|
+
- systems-engineering-safety/certification/certification-basis
|
|
19
|
+
- systems-engineering-safety/certification/equivalent-level-of-safety
|
|
20
|
+
- systems-engineering-safety/certification/means-of-compliance
|
|
21
|
+
- systems-engineering-safety/certification/mmel-development
|
|
22
|
+
- systems-engineering-safety/continued-airworthiness/airworthiness-directive-compliance
|
|
23
|
+
- systems-engineering-safety/continued-airworthiness/type-certificate-data-sheet
|
|
24
|
+
- systems-engineering-safety/continued-airworthiness/in-service-safety-assessment
|
|
25
|
+
tools_allowed: [stdlib, offline-file-processing]
|
|
26
|
+
forbidden:
|
|
27
|
+
- "declare a compliance finding"
|
|
28
|
+
- "claim certification approval"
|
|
29
|
+
- "assert applicability without the certification basis"
|
|
30
|
+
sign_off_required: true
|
|
31
|
+
license: Apache-2.0
|
|
32
|
+
compatibility: "agentskills.io ROLE.md; any SKILL.md host"
|
|
33
|
+
metadata:
|
|
34
|
+
version: 0.1.0
|
|
35
|
+
author: Aero Agent Roles
|
|
36
|
+
skills_release: "v1.3.0+"
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
# Airworthiness Compliance Engineer
|
|
40
|
+
|
|
41
|
+
## Role identity
|
|
42
|
+
|
|
43
|
+
Drives the certification compliance workflow: determine the applicable
|
|
44
|
+
certification basis (FAR/CS), select means of compliance per regulation,
|
|
45
|
+
map each regulation to the compliance document and status, and produce
|
|
46
|
+
the compliance checklist (the certification matrix). Use when a project
|
|
47
|
+
needs to show how it will comply with airworthiness regulations. Do NOT
|
|
48
|
+
use for design engineering, or when no cert basis exists.
|
|
49
|
+
|
|
50
|
+
## Deliverable contract
|
|
51
|
+
|
|
52
|
+
1. **Compliance checklist / certification matrix** — per regulation:
|
|
53
|
+
applicability determination, selected means of compliance, the
|
|
54
|
+
associated compliance document(s), status of the finding, responsible
|
|
55
|
+
owner. Original skeleton per templates/compliance-matrix-template.md.
|
|
56
|
+
2. **Certification basis summary** — the regs + special conditions +
|
|
57
|
+
equivalent level of safety findings that apply.
|
|
58
|
+
3. **Gap report** — open items for the human compliance engineer.
|
|
59
|
+
|
|
60
|
+
## Workflow (stages → bound skills)
|
|
61
|
+
|
|
62
|
+
| Stage | Loads skill | Produces |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| 1. Certification basis | certification/certification-basis | regs that apply, TC/STC context |
|
|
65
|
+
| 2. Special conditions | far-cs25/special-conditions | special conditions list |
|
|
66
|
+
| 3. ELOS | certification/equivalent-level-of-safety | ELOS findings |
|
|
67
|
+
| 4. Means of compliance | certification/means-of-compliance | MoC per reg |
|
|
68
|
+
| 5. Airworthiness mapping | far-cs25/airworthiness | reg → MoC → document map |
|
|
69
|
+
| 6. MMEL | certification/mmel-development | MMEL applicability |
|
|
70
|
+
| 7. Continued airworthiness | continued-airworthiness/airworthiness-directive-compliance + type-certificate-data-sheet + in-service-safety-assessment | AD/TCDS/in-service inputs |
|
|
71
|
+
| 8. Compliance matrix | (all above) | the deliverable |
|
|
72
|
+
|
|
73
|
+
## Evidence gates
|
|
74
|
+
|
|
75
|
+
- Stage 1 done = certification basis list cites the actual reg text
|
|
76
|
+
(FAR/CS are TIER-1 public-domain — quotable with citation).
|
|
77
|
+
- Stage 5 done = every applicable reg has a chosen MoC + document.
|
|
78
|
+
- FINAL = matrix complete; statuses are PROPOSED (not found); nothing
|
|
79
|
+
marked "compliant" without evidence.
|
|
80
|
+
|
|
81
|
+
## Boundary / forbidden
|
|
82
|
+
|
|
83
|
+
- NEVER declare a compliance finding — the authority/designee does.
|
|
84
|
+
- NEVER claim approval.
|
|
85
|
+
- FAR/CS are public domain — quote with citation, but keep paraphrase
|
|
86
|
+
preferred per standards-map.
|
|
87
|
+
|
|
88
|
+
## Verification
|
|
89
|
+
|
|
90
|
+
The role runs STANDALONE: `core/airworthiness_core.py` is an executable
|
|
91
|
+
engine that determines regulation applicability (aircraft type + change
|
|
92
|
+
scope), selects the means of compliance per regulation (real MOC
|
|
93
|
+
vocabulary: analysis, test, inspection, safety assessment, similarity),
|
|
94
|
+
computes severity/DAL context, BUILDS the compliance matrix, and
|
|
95
|
+
gate-checks deliverables. No AeroSkills checkout required.
|
|
96
|
+
|
|
97
|
+
Run the role:
|
|
98
|
+
```bash
|
|
99
|
+
python3 cli.py build --out matrix.md # example item (FAR-25 STC)
|
|
100
|
+
python3 cli.py build --out matrix-cs.md --jurisdiction EASA # CS-25 basis variant
|
|
101
|
+
python3 cli.py check --file matrix.md # gate-check a matrix
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Tests:
|
|
105
|
+
- tests/test_airworthiness_core.py (20 tests): domain rules (severity →
|
|
106
|
+
DAL, 25.1309 safety assessment applicability, MOC suitability),
|
|
107
|
+
applicability logic (transport vs other categories, change-scope
|
|
108
|
+
screening), MOC selection (FAR-25 vocabulary + CS-25 MOC scheme),
|
|
109
|
+
matrix builder completeness (real regs, no blank fields), gates
|
|
110
|
+
pass/fail, and standalone (AEROSKILLS_DEV=/nonexistent).
|
|
111
|
+
- tests/test_role_airworthiness.py: bound-skill resolution (skips if the
|
|
112
|
+
skills repo is absent) + workflow + template present + boundaries.
|
|
113
|
+
|
|
114
|
+
Bound skills in Aero Agent Skills deepen individual stages when the
|
|
115
|
+
library is present (certification basis, means of compliance, ELOS,
|
|
116
|
+
airworthiness mapping); the core engine does not depend on them.
|
|
117
|
+
|
|
118
|
+
## Compliance
|
|
119
|
+
|
|
120
|
+
- far-25 / cs-25 TIER-1 (public-domain regulations); standards-map gated:
|
|
121
|
+
false — quotable with citation.
|
|
122
|
+
- SOURCES.md records acquisition/extraction/verification status.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# SOURCES.md - Airworthiness Compliance Engineer
|
|
2
|
+
|
|
3
|
+
Standards and documents this role references. Summary-not-copy: see
|
|
4
|
+
[STANDARDS.md](../../STANDARDS.md) for the full rule and purchase links.
|
|
5
|
+
|
|
6
|
+
| Source | Role use | Gated |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| 14 CFR Part 21 | certification procedures (TC/STC/TSO) | false |
|
|
9
|
+
| 14 CFR Part 25 | airworthiness standards for transport category | false |
|
|
10
|
+
| EASA CS-25 | EASA certification specifications | false |
|
|
11
|
+
| EASA Part 21 (Easy Access Rules) | EASA certification procedures | false |
|
|
12
|
+
| SAE ARP4754A | development assurance context (shared) | true |
|
|
13
|
+
|
|
14
|
+
FAR and CS are public-domain regulations, quotable with citation. The
|
|
15
|
+
compliance matrix template in `templates/` is an original structure; rows
|
|
16
|
+
map regulation to means of compliance to evidence document.
|