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.
Files changed (170) hide show
  1. package/NOTICE +18 -0
  2. package/README.md +49 -0
  3. package/bin/aero-agent-roles.js +127 -0
  4. package/lib/catalog.js +97 -0
  5. package/lib/install.js +48 -0
  6. package/lib/mcp.js +199 -0
  7. package/manifest.json +516 -0
  8. package/package.json +44 -0
  9. package/roles/aerodynamics-engineer/ROLE.md +153 -0
  10. package/roles/aerodynamics-engineer/SOURCES.md +12 -0
  11. package/roles/aerodynamics-engineer/cli.py +143 -0
  12. package/roles/aerodynamics-engineer/core/aerodynamics_core.py +1218 -0
  13. package/roles/aerodynamics-engineer/templates/aero-design-report-template.md +72 -0
  14. package/roles/aerodynamics-engineer/tests/test_aerodynamics_core.py +227 -0
  15. package/roles/aerodynamics-engineer/tests/test_role_aerodynamics.py +94 -0
  16. package/roles/aircraft-design-engineer/ROLE.md +138 -0
  17. package/roles/aircraft-design-engineer/SOURCES.md +12 -0
  18. package/roles/aircraft-design-engineer/cli.py +152 -0
  19. package/roles/aircraft-design-engineer/core/aircraft_design_core.py +1278 -0
  20. package/roles/aircraft-design-engineer/templates/concept-design-template.md +99 -0
  21. package/roles/aircraft-design-engineer/tests/test_aircraft_design_core.py +269 -0
  22. package/roles/aircraft-design-engineer/tests/test_role_aircraft_design_engineer.py +106 -0
  23. package/roles/airworthiness-compliance-engineer/ROLE.md +122 -0
  24. package/roles/airworthiness-compliance-engineer/SOURCES.md +16 -0
  25. package/roles/airworthiness-compliance-engineer/cli.py +146 -0
  26. package/roles/airworthiness-compliance-engineer/core/airworthiness_core.py +894 -0
  27. package/roles/airworthiness-compliance-engineer/templates/compliance-matrix-template.md +46 -0
  28. package/roles/airworthiness-compliance-engineer/tests/test_airworthiness_core.py +224 -0
  29. package/roles/airworthiness-compliance-engineer/tests/test_role_airworthiness.py +85 -0
  30. package/roles/as9100-quality-auditor/ROLE.md +128 -0
  31. package/roles/as9100-quality-auditor/SOURCES.md +16 -0
  32. package/roles/as9100-quality-auditor/cli.py +136 -0
  33. package/roles/as9100-quality-auditor/core/as9100_core.py +857 -0
  34. package/roles/as9100-quality-auditor/templates/findings-report-template.md +119 -0
  35. package/roles/as9100-quality-auditor/tests/test_as9100_cli_profile.py +105 -0
  36. package/roles/as9100-quality-auditor/tests/test_as9100_core.py +204 -0
  37. package/roles/as9100-quality-auditor/tests/test_role_as9100.py +90 -0
  38. package/roles/data-bus-avionics-engineer/ROLE.md +146 -0
  39. package/roles/data-bus-avionics-engineer/SOURCES.md +23 -0
  40. package/roles/data-bus-avionics-engineer/cli.py +311 -0
  41. package/roles/data-bus-avionics-engineer/core/data_bus_avionics_core.py +1121 -0
  42. package/roles/data-bus-avionics-engineer/templates/bus-assessment-template.md +134 -0
  43. package/roles/data-bus-avionics-engineer/tests/test_bundle_protocol.py +137 -0
  44. package/roles/data-bus-avionics-engineer/tests/test_data_bus_avionics_core.py +261 -0
  45. package/roles/data-bus-avionics-engineer/tests/test_role_data_bus_avionics_engineer.py +121 -0
  46. package/roles/do160-environmental-engineer/ROLE.md +136 -0
  47. package/roles/do160-environmental-engineer/SOURCES.md +30 -0
  48. package/roles/do160-environmental-engineer/cli.py +147 -0
  49. package/roles/do160-environmental-engineer/core/do160_environmental_core.py +1246 -0
  50. package/roles/do160-environmental-engineer/templates/environmental-qualification-plan-template.md +114 -0
  51. package/roles/do160-environmental-engineer/tests/test_do160_environmental_core.py +312 -0
  52. package/roles/do160-environmental-engineer/tests/test_role_do160_environmental_engineer.py +281 -0
  53. package/roles/do178c-cert-engineer/ROLE.md +120 -0
  54. package/roles/do178c-cert-engineer/SOURCES.md +16 -0
  55. package/roles/do178c-cert-engineer/cli.py +129 -0
  56. package/roles/do178c-cert-engineer/core/do178c_core.py +411 -0
  57. package/roles/do178c-cert-engineer/templates/psac-template.md +74 -0
  58. package/roles/do178c-cert-engineer/tests/test_do178c_cert_engineer_core.py +91 -0
  59. package/roles/do178c-cert-engineer/tests/test_role_do178c.py +109 -0
  60. package/roles/do254-hardware-engineer/ROLE.md +127 -0
  61. package/roles/do254-hardware-engineer/SOURCES.md +19 -0
  62. package/roles/do254-hardware-engineer/cli.py +308 -0
  63. package/roles/do254-hardware-engineer/core/do254_hardware_core.py +761 -0
  64. package/roles/do254-hardware-engineer/templates/phac-template.md +87 -0
  65. package/roles/do254-hardware-engineer/tests/test_do254_bundle_profile.py +152 -0
  66. package/roles/do254-hardware-engineer/tests/test_do254_hardware_core.py +200 -0
  67. package/roles/do254-hardware-engineer/tests/test_role_do254_hardware.py +108 -0
  68. package/roles/engineering-analysis-engineer/ROLE.md +154 -0
  69. package/roles/engineering-analysis-engineer/SOURCES.md +15 -0
  70. package/roles/engineering-analysis-engineer/cli.py +151 -0
  71. package/roles/engineering-analysis-engineer/core/engineering_analysis_engineer_core.py +866 -0
  72. package/roles/engineering-analysis-engineer/templates/analysis-memo-template.md +73 -0
  73. package/roles/engineering-analysis-engineer/tests/test_engineering_analysis_engineer_core.py +367 -0
  74. package/roles/engineering-analysis-engineer/tests/test_role_engineering_analysis_engineer.py +113 -0
  75. package/roles/flight-management-engineer/ROLE.md +160 -0
  76. package/roles/flight-management-engineer/SOURCES.md +30 -0
  77. package/roles/flight-management-engineer/cli.py +258 -0
  78. package/roles/flight-management-engineer/core/flight_management_core.py +1473 -0
  79. package/roles/flight-management-engineer/templates/route-assessment-template.md +94 -0
  80. package/roles/flight-management-engineer/tests/test_flight_management_core.py +437 -0
  81. package/roles/flight-management-engineer/tests/test_role_flight_management_engineer.py +123 -0
  82. package/roles/flight-mechanics-engineer/ROLE.md +162 -0
  83. package/roles/flight-mechanics-engineer/SOURCES.md +51 -0
  84. package/roles/flight-mechanics-engineer/cli.py +149 -0
  85. package/roles/flight-mechanics-engineer/core/flight_mechanics_core.py +1507 -0
  86. package/roles/flight-mechanics-engineer/templates/perf-sc-report-template.md +82 -0
  87. package/roles/flight-mechanics-engineer/tests/test_flight_mechanics_core.py +277 -0
  88. package/roles/flight-mechanics-engineer/tests/test_role_flight_mechanics_engineer.py +141 -0
  89. package/roles/flight-test-engineer/ROLE.md +137 -0
  90. package/roles/flight-test-engineer/SOURCES.md +24 -0
  91. package/roles/flight-test-engineer/cli.py +135 -0
  92. package/roles/flight-test-engineer/core/flight_test_core.py +931 -0
  93. package/roles/flight-test-engineer/templates/flight-test-template.md +143 -0
  94. package/roles/flight-test-engineer/tests/test_flight_test_engineer_core.py +227 -0
  95. package/roles/flight-test-engineer/tests/test_role_flight_test_engineer.py +150 -0
  96. package/roles/flight-test-performance-engineer/ROLE.md +165 -0
  97. package/roles/flight-test-performance-engineer/SOURCES.md +20 -0
  98. package/roles/flight-test-performance-engineer/cli.py +319 -0
  99. package/roles/flight-test-performance-engineer/core/flight_test_performance_core.py +1398 -0
  100. package/roles/flight-test-performance-engineer/templates/performance-report-template.md +105 -0
  101. package/roles/flight-test-performance-engineer/tests/test_flight_test_performance_engineer_core.py +308 -0
  102. package/roles/flight-test-performance-engineer/tests/test_role_flight_test_performance_engineer.py +165 -0
  103. package/roles/gnc-engineer/ROLE.md +141 -0
  104. package/roles/gnc-engineer/SOURCES.md +35 -0
  105. package/roles/gnc-engineer/cli.py +141 -0
  106. package/roles/gnc-engineer/core/gnc_core.py +1074 -0
  107. package/roles/gnc-engineer/templates/gnc-report-template.md +105 -0
  108. package/roles/gnc-engineer/tests/test_gnc_engineer_core.py +238 -0
  109. package/roles/gnc-engineer/tests/test_role_gnc_engineer.py +142 -0
  110. package/roles/guidance-engineer/ROLE.md +156 -0
  111. package/roles/guidance-engineer/SOURCES.md +43 -0
  112. package/roles/guidance-engineer/cli.py +280 -0
  113. package/roles/guidance-engineer/core/guidance_core.py +820 -0
  114. package/roles/guidance-engineer/templates/guidance-report-template.md +70 -0
  115. package/roles/guidance-engineer/tests/test_guidance_engineer_core.py +269 -0
  116. package/roles/guidance-engineer/tests/test_role_guidance_engineer.py +146 -0
  117. package/roles/high-speed-aerodynamics-engineer/ROLE.md +161 -0
  118. package/roles/high-speed-aerodynamics-engineer/SOURCES.md +18 -0
  119. package/roles/high-speed-aerodynamics-engineer/cli.py +282 -0
  120. package/roles/high-speed-aerodynamics-engineer/core/high_speed_aero_core.py +1566 -0
  121. package/roles/high-speed-aerodynamics-engineer/templates/high-speed-memo-template.md +75 -0
  122. package/roles/high-speed-aerodynamics-engineer/tests/test_bundle_protocol.py +103 -0
  123. package/roles/high-speed-aerodynamics-engineer/tests/test_high_speed_aero_core.py +280 -0
  124. package/roles/high-speed-aerodynamics-engineer/tests/test_role_high_speed_aerodynamics.py +125 -0
  125. package/roles/ndt-engineer/ROLE.md +157 -0
  126. package/roles/ndt-engineer/SOURCES.md +18 -0
  127. package/roles/ndt-engineer/cli.py +316 -0
  128. package/roles/ndt-engineer/core/ndt_core.py +1590 -0
  129. package/roles/ndt-engineer/templates/ndt-plan-template.md +109 -0
  130. package/roles/ndt-engineer/tests/test_ndt_cli_bundle.py +183 -0
  131. package/roles/ndt-engineer/tests/test_ndt_core.py +437 -0
  132. package/roles/ndt-engineer/tests/test_role_ndt_engineer.py +148 -0
  133. package/roles/numerical-analysis-engineer/ROLE.md +158 -0
  134. package/roles/numerical-analysis-engineer/SOURCES.md +19 -0
  135. package/roles/numerical-analysis-engineer/cli.py +414 -0
  136. package/roles/numerical-analysis-engineer/core/numerical_analysis_core.py +1220 -0
  137. package/roles/numerical-analysis-engineer/templates/verification-memo-template.md +120 -0
  138. package/roles/numerical-analysis-engineer/tests/test_bundle_protocol.py +157 -0
  139. package/roles/numerical-analysis-engineer/tests/test_numerical_analysis_core.py +337 -0
  140. package/roles/numerical-analysis-engineer/tests/test_role_numerical_analysis_engineer.py +117 -0
  141. package/roles/propulsion-engineer/ROLE.md +163 -0
  142. package/roles/propulsion-engineer/SOURCES.md +15 -0
  143. package/roles/propulsion-engineer/cli.py +168 -0
  144. package/roles/propulsion-engineer/core/propulsion_core.py +1291 -0
  145. package/roles/propulsion-engineer/templates/propulsion-report-template.md +92 -0
  146. package/roles/propulsion-engineer/tests/test_propulsion_engineer_core.py +294 -0
  147. package/roles/propulsion-engineer/tests/test_role_propulsion_engineer.py +106 -0
  148. package/roles/space-systems-engineer/ROLE.md +161 -0
  149. package/roles/space-systems-engineer/SOURCES.md +49 -0
  150. package/roles/space-systems-engineer/cli.py +173 -0
  151. package/roles/space-systems-engineer/core/space_systems_core.py +980 -0
  152. package/roles/space-systems-engineer/templates/space-systems-template.md +88 -0
  153. package/roles/space-systems-engineer/tests/test_role_space_systems_engineer.py +117 -0
  154. package/roles/space-systems-engineer/tests/test_space_systems_engineer_core.py +247 -0
  155. package/roles/state-estimation-engineer/ROLE.md +154 -0
  156. package/roles/state-estimation-engineer/SOURCES.md +27 -0
  157. package/roles/state-estimation-engineer/cli.py +326 -0
  158. package/roles/state-estimation-engineer/core/state_estimation_core.py +958 -0
  159. package/roles/state-estimation-engineer/templates/nav-state-estimator-design-template.md +76 -0
  160. package/roles/state-estimation-engineer/tests/test_bundle_protocol.py +110 -0
  161. package/roles/state-estimation-engineer/tests/test_role_state_estimation_engineer.py +127 -0
  162. package/roles/state-estimation-engineer/tests/test_state_estimation_engineer_core.py +231 -0
  163. package/roles/structures-loads-engineer/ROLE.md +171 -0
  164. package/roles/structures-loads-engineer/SOURCES.md +44 -0
  165. package/roles/structures-loads-engineer/cli.py +219 -0
  166. package/roles/structures-loads-engineer/core/structures_loads_core.py +1112 -0
  167. package/roles/structures-loads-engineer/templates/loads-strength-report-template.md +96 -0
  168. package/roles/structures-loads-engineer/tests/test_bundle_protocol.py +101 -0
  169. package/roles/structures-loads-engineer/tests/test_role_structures_loads_engineer.py +147 -0
  170. package/roles/structures-loads-engineer/tests/test_structures_loads_core.py +261 -0
@@ -0,0 +1,143 @@
1
+ # Flight Test Plan and Envelope Expansion Report
2
+
3
+ **Aircraft:** FT-1 Worked-Example Light Jet
4
+ **Basis:** FAR/CS-25 context (worked example)
5
+ **Status:** draft-for-review
6
+
7
+ _Flight data below are SIMULATED for the worked example and must be replaced by recorded flight data._
8
+
9
+ ## 1. Objectives and configuration
10
+
11
+ - Determine VS1g and the V-speed set (vref/v2/vr/VA) with warning-margin evidence in every configuration
12
+ - Demonstrate freedom from flutter to V_D with damping >= 0.03 at the maximum test speed and an extrapolated margin >= 1.2*V_D
13
+ - Expand the speed and load factor envelope in build-up blocks to V_D / n_max with a load factor check at every point
14
+ - Verify stall recovery characteristics within the program limits
15
+ - Measure climb performance and check the gradient requirement
16
+
17
+ - Limit load factor: n_max = 2.5 g positive. Stall speeds (EAS): VS1g = 44.0 m/s (86 kt), VS1 = 40.0 m/s (78 kt), VS0 = 36.0 m/s (70 kt).
18
+ - Placards (EAS): VFE = 62.0 m/s, VNO = 85.0 m/s, VNE = 94.0 m/s, V_D = 106.0 m/s, MMO = 0.78.
19
+ - Derived: VA = 69.6 m/s (135 kt), vref = 46.8 m/s (91 kt), v2 = 48.0 m/s (93 kt), vr = 44.0 m/s (86 kt).
20
+ - Crew: test pilot + flight test engineer console; airspace: restricted area; instrumentation: calibrated pitot-static, accelerometers, strain gauges on wing/elevator, flutter excitation vanes, telemetry with real-time damping.
21
+
22
+ ## 2. Safety
23
+
24
+ | Hazard | Sev | Lik | Risk | High (>=15) | Mitigation |
25
+ |---|---|---|---|---|---|
26
+ | Flutter onset at high speed | 5 | 2 | 10 | no | GVT + build-up blocks, damping gate 0.03, excitation vanes, real-time monitoring |
27
+ | Stall departure at aft CG | 4 | 3 | 12 | no | entry technique, altitude floor, recovery demonstration, spin chute installed |
28
+ | Overspeed control upset | 4 | 2 | 8 | no | abort criteria at every block, test pilot briefing, VMO/VNE marking |
29
+ | Engine failure during climb segment | 3 | 2 | 6 | no | single-engine drills, field/landing options, engine instruments monitored |
30
+ | Telemetry data loss | 2 | 3 | 6 | no | onboard recording backup, link checked pre-flight |
31
+
32
+ Every high-risk item is mitigated before the flight: true.
33
+
34
+ | Go/no-go criterion | Value |
35
+ |---|---|
36
+ | weather | GO |
37
+ | aircraft_ready | GO |
38
+ | instrumentation_ok | GO |
39
+ | safety_review_ok | GO |
40
+ | airspace_ok | GO |
41
+
42
+ Flight release: GO.
43
+
44
+ ## 3. Build-up plan
45
+
46
+ Build-up order: GVT -> flutter build-up (blocks S1..S4) -> stall matrix -> load factor envelope -> climb performance. Each block clears before the next is flown; every block states its limit, abort criteria, and PASS/FAIL criteria.
47
+
48
+ ### 3.1 Flutter build-up (speed blocks as fractions of V_D)
49
+
50
+ | Block | V_D fraction | Target (EAS) | Category | Abort | PASS/FAIL criteria |
51
+ |---|---|---|---|---|---|
52
+ | S1 | 0.85 | 90.1 m/s (175 kt) | vno-to-vne | immediate pull-up + throttle reduction if buffet, LCO, or control anomaly | PASS: damping >= 0.03 at point, no LCO, load factor within 2.5 g; FAIL otherwise |
53
+ | S2 | 0.90 | 95.4 m/s (185 kt) | at-or-above-vne | immediate pull-up + throttle reduction if buffet, LCO, or control anomaly | PASS: damping >= 0.03 at point, no LCO, load factor within 2.5 g; FAIL otherwise |
54
+ | S3 | 0.95 | 100.7 m/s (196 kt) | at-or-above-vne | immediate pull-up + throttle reduction if buffet, LCO, or control anomaly | PASS: damping >= 0.03 at point, no LCO, load factor within 2.5 g; FAIL otherwise |
55
+ | S4 | 1.00 | 106.0 m/s (206 kt) | at-or-above-vne | immediate pull-up + throttle reduction if buffet, LCO, or control anomaly | PASS: damping >= 0.03 at point, no LCO, load factor within 2.5 g; FAIL otherwise |
56
+
57
+ Required flutter demonstration speed: V_F = 127.2 m/s (1.2 x V_D). Damping >= 0.03 required at the maximum test speed; structural mode frequency separation >= 0.10 at every point.
58
+
59
+ ### 3.2 Altitude / Mach grid (V_D tested per altitude, ISA)
60
+
61
+ | Altitude | Density ratio | Speed of sound | TAS at V_D | Mach at V_D | MMO | Limit |
62
+ |---|---|---|---|---|---|---|
63
+ | 0 ft | 1.000 | 340.3 m/s | 106.0 m/s | 0.311 | 0.78 | eas-limited |
64
+ | 25000 ft | 0.448 | 309.7 m/s | 158.3 m/s | 0.511 | 0.78 | eas-limited |
65
+ | 35000 ft | 0.310 | 296.5 m/s | 190.4 m/s | 0.642 | 0.78 | eas-limited |
66
+
67
+ ### 3.3 Stall matrix (VS1g and warning onset per configuration)
68
+
69
+ | Config | Measured VS1g (EAS) | Warning required | Warning onset | Recovery limits |
70
+ |---|---|---|---|---|
71
+ | clean | 44.1 m/s | 46.3 m/s | 46.8 m/s | alt <= 30 m, pitch <= 8 deg, roll <= 20 deg |
72
+ | takeoff (flaps) | 40.4 m/s | 42.4 m/s | 42.5 m/s | alt <= 30 m, pitch <= 8 deg, roll <= 20 deg |
73
+ | landing (flaps/gear) | 36.3 m/s | 38.1 m/s | 38.2 m/s | alt <= 30 m, pitch <= 8 deg, roll <= 20 deg |
74
+
75
+ ### 3.4 Load factor envelope (pull blocks at VA or above)
76
+
77
+ | Block | n_max fraction | Target n | PASS/FAIL |
78
+ |---|---|---|---|
79
+ | L1 | 0.50 | 1.25 g | PASS if n <= 2.5 g at point, with no buffet/LCO |
80
+ | L2 | 0.75 | 1.88 g | PASS if n <= 2.5 g at point, with no buffet/LCO |
81
+ | L3 | 1.00 | 2.50 g | PASS if n <= 2.5 g at point, with no buffet/LCO |
82
+
83
+ ### 3.5 Climb performance
84
+
85
+ Sawtooth climb segments at constant indicated airspeed; gradient check against 2.4 % (two-engine context).
86
+
87
+ ## 4. Results
88
+
89
+ ### 4.1 Flutter build-up points
90
+
91
+ | Block | Speed (EAS) | Damping | Min damping | Verdict |
92
+ |---|---|---|---|---|
93
+ | S1 | 90.1 m/s | 0.120 | 0.030 | PASS |
94
+ | S2 | 95.4 m/s | 0.115 | 0.030 | PASS |
95
+ | S3 | 100.7 m/s | 0.108 | 0.030 | PASS |
96
+ | S4 | 106.0 m/s | 0.100 | 0.030 | PASS |
97
+
98
+ Extrapolated flutter speed from the damping trend: 185.7 m/s; margin ratio 1.75 vs required 1.2 -> PASS.
99
+
100
+ Structural mode frequency separation (GVT pairs):
101
+ - mode pair 4.2 Hz / 11.6 Hz: separation 0.94 >= 0.10 -> PASS
102
+ - mode pair 9.1 Hz / 14.8 Hz: separation 0.48 >= 0.10 -> PASS
103
+ - mode pair 11.6 Hz / 14.8 Hz: separation 0.24 >= 0.10 -> PASS
104
+ - mode pair 4.2 Hz / 9.1 Hz: separation 0.74 >= 0.10 -> PASS
105
+
106
+ ### 4.2 Stall results
107
+
108
+ | Config | VS1g (EAS) | Warning onset | Warning margin | Recovery | Verdict |
109
+ |---|---|---|---|---|---|
110
+ | clean | 44.1 m/s | 46.8 m/s | +2.70 m/s | alt 18 m / pitch 3 deg / roll 6 deg | PASS |
111
+ | takeoff (flaps) | 40.4 m/s | 42.5 m/s | +2.10 m/s | alt 21 m / pitch 4 deg / roll 8 deg | PASS |
112
+ | landing (flaps/gear) | 36.3 m/s | 38.2 m/s | +1.90 m/s | alt 24 m / pitch 5 deg / roll 9 deg | PASS |
113
+
114
+ ### 4.3 Load factor envelope results
115
+
116
+ | Block | Target n | Achieved n | Limit | Verdict |
117
+ |---|---|---|---|---|
118
+ | L1 | 1.25 g | 1.26 g | 2.5 g | PASS |
119
+ | L2 | 1.88 g | 1.88 g | 2.5 g | PASS |
120
+ | L3 | 2.50 g | 2.48 g | 2.5 g | PASS |
121
+
122
+ ### 4.4 Climb performance results
123
+
124
+ - Rate of climb: 1450 ft/min (segment reduction, corrected to reference weight).
125
+ - Gradient: 2.70 % vs 2.4 % required -> margin +0.30 % (PASS).
126
+
127
+ ## 5. Findings, limit changes, and recommendations
128
+
129
+ | Envelope limit change | Traced to data point | Verdict |
130
+ |---|---|---|
131
+ | Speed envelope to 0.85 V_D | S1 | PASS |
132
+ | Speed envelope to 0.90 V_D | S2 | PASS |
133
+ | Speed envelope to 0.95 V_D | S3 | PASS |
134
+ | Speed envelope to 1.00 V_D | S4 | PASS |
135
+
136
+ Recommendations: (1) expand to the top block only after the damping and separation verdicts above; (2) repeat the configuration whose stall warning onset is marginal before envelope use; (3) confirm flutter clearance per point with the flight test conductor.
137
+
138
+ ## 6. Sign-off
139
+
140
+ This plan/report states that the recorded data support expanding to the listed limits PENDING human flight test conductor review. It is a draft deliverable, not a clearance.
141
+
142
+ ---
143
+ *Generated by Aero Agent Roles flight-test-engineer core (2026-09-05). DRAFT for human flight test conductor review. Not a clearance and not an approval of the envelope or of flight.*
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env python3
2
+ """Test flight_test_core: the executable engine of the flight-test-engineer role.
3
+
4
+ Proves the role can DO its job standalone (no AeroSkills needed):
5
+ V-speed margins, corner speed, stall warning/recovery rules, flutter
6
+ build-up with the damping requirement and margin ratio, altitude/Mach
7
+ grid, safety/go-no-go, deliverable generation, and evidence-gate checks.
8
+ """
9
+ import os
10
+ import sys
11
+ import unittest
12
+
13
+ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
14
+ "..", "core"))
15
+ from flight_test_core import (
16
+ MIN_DAMPING, MIN_FREQ_SEPARATION, accelerated_stall_speed,
17
+ build_flight_test_deliverable, check_flight_test_deliverable,
18
+ check_flight_test_markdown, classify_airspeed, corner_speed,
19
+ damping_margin, example_aircraft, example_deliverable_markdown,
20
+ example_flight_data, flutter_margin_ratio, flutter_required_speed,
21
+ flutter_speed_from_damping, frequency_separation, isa_density_ratio,
22
+ level_turn_load_factor, mach_grid_row, render_flight_test_markdown,
23
+ risk_index, speed_block_targets, stall_recovery_verdict,
24
+ stall_warning_required, stall_warning_verdict, v_speeds,
25
+ )
26
+
27
+
28
+ class TestDomainRules(unittest.TestCase):
29
+
30
+ def test_v_speed_factors(self):
31
+ ac = example_aircraft()
32
+ s = v_speeds(ac)
33
+ # vref = 1.3*vs0 = 46.8, v2 = 1.2*vs1 = 48.0, vr = 1.1*vs1 = 44.0
34
+ self.assertAlmostEqual(s["vref"], 46.8, places=3)
35
+ self.assertAlmostEqual(s["v2"], 48.0, places=3)
36
+ self.assertAlmostEqual(s["vr"], 44.0, places=3)
37
+
38
+ def test_corner_speed(self):
39
+ # VA = VS * sqrt(n_max): 44 * sqrt(2.5)
40
+ self.assertAlmostEqual(corner_speed(44.0, 2.5),
41
+ 44.0 * 2.5 ** 0.5, places=3)
42
+ with self.assertRaises(ValueError):
43
+ corner_speed(0, 2.5)
44
+ with self.assertRaises(ValueError):
45
+ corner_speed(44.0, 1.0)
46
+
47
+ def test_placard_ordering_validation(self):
48
+ ac = example_aircraft()
49
+ self.assertAlmostEqual(v_speeds(ac)["va"], corner_speed(44.0, 2.5))
50
+ ac.vne_eas_ms = 50.0 # breaks vfe < va < vno < vne < vd
51
+ with self.assertRaises(ValueError):
52
+ build_flight_test_deliverable(ac)
53
+
54
+ def test_airspeed_classification(self):
55
+ ac = example_aircraft()
56
+ va = v_speeds(ac)["va"]
57
+ self.assertEqual(classify_airspeed(60, ac.vfe_eas_ms, va,
58
+ ac.vno_eas_ms, ac.vne_eas_ms),
59
+ "below-vfe")
60
+ self.assertEqual(classify_airspeed(90.1, ac.vfe_eas_ms, va,
61
+ ac.vno_eas_ms, ac.vne_eas_ms),
62
+ "vno-to-vne")
63
+ self.assertEqual(classify_airspeed(95.4, ac.vfe_eas_ms, va,
64
+ ac.vno_eas_ms, ac.vne_eas_ms),
65
+ "at-or-above-vne")
66
+
67
+ def test_stall_warning_margin(self):
68
+ # 1.05 * VS1g or VS1g + 3 kt, whichever is greater
69
+ req = stall_warning_required(44.0)
70
+ self.assertAlmostEqual(req, max(44.0 * 1.05, 44.0 + 3 * 0.514444),
71
+ places=3)
72
+ self.assertTrue(stall_warning_verdict(44.0, req + 0.1)["ok"])
73
+ self.assertFalse(stall_warning_verdict(44.0, req - 0.1)["ok"])
74
+
75
+ def test_accelerated_stall_and_bank(self):
76
+ # 60 deg bank doubles load factor: stall rises ~41%
77
+ n60 = level_turn_load_factor(60.0)
78
+ self.assertAlmostEqual(n60, 2.0, places=4)
79
+ v = accelerated_stall_speed(44.0, n60)
80
+ self.assertAlmostEqual(v, 44.0 * 2.0 ** 0.5, places=3)
81
+ with self.assertRaises(ValueError):
82
+ accelerated_stall_speed(44.0, 0.9)
83
+
84
+ def test_stall_recovery_verdict(self):
85
+ ac = example_aircraft()
86
+ good = stall_recovery_verdict(ac, 18.0, 3.0, 6.0)
87
+ self.assertTrue(good["ok"])
88
+ bad = stall_recovery_verdict(ac, 18.0, 12.0, 6.0) # pitch-up excess
89
+ self.assertFalse(bad["ok"])
90
+ self.assertFalse(bad["pitch_up_ok"])
91
+
92
+ def test_flutter_required_speed(self):
93
+ ac = example_aircraft()
94
+ self.assertAlmostEqual(flutter_required_speed(ac.vd_eas_ms),
95
+ 1.2 * 106.0, places=3)
96
+
97
+ def test_damping_margin_rule(self):
98
+ self.assertTrue(damping_margin(0.100)["pass"])
99
+ self.assertTrue(damping_margin(0.03)["pass"]) # boundary passes
100
+ self.assertFalse(damping_margin(0.02)["pass"])
101
+ self.assertEqual(MIN_DAMPING, 0.03)
102
+
103
+ def test_frequency_separation(self):
104
+ r = frequency_separation(11.6, 14.8)
105
+ self.assertAlmostEqual(r["separation"], 3.2 / 13.2, places=3)
106
+ self.assertTrue(r["pass"])
107
+ self.assertEqual(MIN_FREQ_SEPARATION, 0.10)
108
+
109
+ def test_flutter_extrapolation_and_margin(self):
110
+ # simulated damping trend over the block speeds
111
+ vf = flutter_speed_from_damping([90.1, 95.4, 100.7, 106.0],
112
+ [0.120, 0.115, 0.108, 0.100])
113
+ self.assertGreater(vf, 106.0 * 1.2) # clears the 1.2 margin
114
+ m = flutter_margin_ratio(vf, 106.0)
115
+ self.assertTrue(m["pass"])
116
+ with self.assertRaises(ValueError):
117
+ flutter_speed_from_damping([90.1, 95.4], [0.1, 0.12]) # rising
118
+
119
+ def test_risk_index_high_threshold(self):
120
+ self.assertEqual(risk_index(5, 3), 15) # high risk boundary
121
+ self.assertEqual(risk_index(5, 2), 10) # mitigated in example
122
+ with self.assertRaises(ValueError):
123
+ risk_index(6, 1)
124
+ with self.assertRaises(ValueError):
125
+ risk_index(0, 1)
126
+
127
+ def test_mach_grid(self):
128
+ g0 = mach_grid_row(example_aircraft(), 0.0, 106.0)
129
+ self.assertAlmostEqual(g0["sigma"], 1.0, places=3)
130
+ self.assertLess(g0["mach"], g0["mmo"]) # EAS-limited at SL
131
+ # density ratio at 11000 m is the standard ~0.297 troposphere top
132
+ self.assertAlmostEqual(isa_density_ratio(11000.0), 0.29707,
133
+ places=3)
134
+
135
+ def test_speed_blocks_equal_steps_to_limit(self):
136
+ blocks = speed_block_targets(106.0)
137
+ fracs = [b["fraction"] for b in blocks]
138
+ self.assertEqual(fracs, [0.85, 0.90, 0.95, 1.00])
139
+ self.assertAlmostEqual(blocks[-1]["target_eas_ms"], 106.0)
140
+ with self.assertRaises(ValueError):
141
+ speed_block_targets(106.0, (0.85, 0.95, 1.00)) # uneven steps
142
+
143
+
144
+ class TestDeliverableBuilder(unittest.TestCase):
145
+
146
+ def test_example_model_complete(self):
147
+ model = build_flight_test_deliverable(example_aircraft())
148
+ self.assertEqual(model["document_type"],
149
+ "Flight Test Plan and Envelope Expansion Report")
150
+ self.assertEqual(model["status"], "draft-for-review")
151
+ self.assertEqual(len(model["speed_blocks"]), 4)
152
+ self.assertEqual(len(model["flutter"]["points"]), 4)
153
+ self.assertEqual(len(model["stall_rows"]), 3)
154
+ self.assertEqual(len(model["n_blocks"]), 3)
155
+ self.assertTrue(model["flutter"]["pass"])
156
+ self.assertTrue(model["stall_all_ok"])
157
+ self.assertTrue(model["go_no_go"]["go"])
158
+ self.assertTrue(model["safety_no_unmitigated"])
159
+
160
+ def test_every_limit_change_traces_to_data_point(self):
161
+ model = build_flight_test_deliverable(example_aircraft())
162
+ for ch in model["limit_changes"]:
163
+ self.assertTrue(ch["data_point"])
164
+ self.assertTrue(ch["ok"])
165
+ # each change maps to a real block id
166
+ ids = {b["id"] for b in model["speed_blocks"]}
167
+ for ch in model["limit_changes"]:
168
+ self.assertIn(ch["data_point"], ids)
169
+
170
+ def test_markdown_has_all_sections(self):
171
+ md = example_deliverable_markdown()
172
+ for sec in ["## 1. Objectives", "## 2. Safety", "## 3. Build-up",
173
+ "## 4. Results", "## 5. Findings", "## 6. Sign-off",
174
+ "### 3.2 Altitude / Mach grid", "### 4.1 Flutter"]:
175
+ self.assertIn(sec, md)
176
+ self.assertIn("not a clearance", md.lower())
177
+ self.assertIn("draft", md.lower())
178
+
179
+ def test_core_gates_all_pass(self):
180
+ model = build_flight_test_deliverable(example_aircraft())
181
+ gates = check_flight_test_deliverable(model)
182
+ self.assertTrue(gates["all_pass"], gates)
183
+
184
+ def test_markdown_gates_all_pass(self):
185
+ md = example_deliverable_markdown()
186
+ gates = check_flight_test_markdown(md)
187
+ self.assertTrue(gates["all_pass"], gates)
188
+
189
+ def test_sign_off_gate_fails_when_status_changed(self):
190
+ model = build_flight_test_deliverable(example_aircraft())
191
+ model["status"] = "cleared"
192
+ gates = check_flight_test_deliverable(model)
193
+ self.assertFalse(gates["all_pass"])
194
+ self.assertFalse(gates["sign_off_honest"])
195
+
196
+ def test_markdown_gate_fails_without_clearance_marker(self):
197
+ md = example_deliverable_markdown()
198
+ import re
199
+ stripped = re.sub(r"(?i)not a clearance", "noted", md)
200
+ self.assertNotIn("not a clearance", stripped.lower())
201
+ gates = check_flight_test_markdown(stripped)
202
+ self.assertFalse(gates["all_pass"])
203
+ self.assertFalse(gates["has_not_clearance"])
204
+
205
+ def test_markdown_gate_fails_with_blank_placeholders(self):
206
+ md = example_deliverable_markdown().replace("clean", "___clean___")
207
+ gates = check_flight_test_markdown(md)
208
+ self.assertFalse(gates["no_blank_blanks"])
209
+
210
+ def test_standalone_no_skills_repo(self):
211
+ # Core must work with no AeroSkills present: set env to a bad path.
212
+ os.environ["AEROSKILLS_DEV"] = "/nonexistent"
213
+ model = build_flight_test_deliverable(example_aircraft())
214
+ self.assertEqual(len(model["speed_blocks"]), 4)
215
+ md = render_flight_test_markdown(model)
216
+ self.assertGreater(len(md), 4000)
217
+ self.assertTrue(check_flight_test_markdown(md)["all_pass"])
218
+
219
+ def test_example_data_must_cover_blocks(self):
220
+ data = example_flight_data()
221
+ data["flutter_points"]["dampings"] = [0.1, 0.1, 0.1]
222
+ with self.assertRaises(ValueError):
223
+ build_flight_test_deliverable(example_aircraft(), data)
224
+
225
+
226
+ if __name__ == "__main__":
227
+ unittest.main()
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env python3
2
+ """Role test: flight-test-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. The deliverable template is a FILLED worked example: complete
9
+ sections, real numbers, no ___ blanks, DRAFT + not-a-clearance.
10
+ 4. The executable core + cli exist and pass their own gates standalone
11
+ (no AeroSkills checkout required).
12
+ """
13
+ import os
14
+ import re
15
+ import sys
16
+ import unittest
17
+
18
+ ROLES_REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(
19
+ os.path.abspath(__file__)))))
20
+ AEROSKILLS = os.environ.get("AEROSKILLS_DEV", os.path.expanduser("~/AeroSkills"))
21
+ HAS_SKILLS = os.path.isdir(os.path.join(AEROSKILLS, "skills"))
22
+ ROLE_DIR = os.path.join(ROLES_REPO, "roles", "flight-test-engineer")
23
+ TEMPLATE = os.path.join(ROLE_DIR, "templates", "flight-test-template.md")
24
+ CORE = os.path.join(ROLE_DIR, "core", "flight_test_core.py")
25
+ CLI = os.path.join(ROLE_DIR, "cli.py")
26
+
27
+ EXPECTED_BOUND = [
28
+ "flight-test-operations/planning/flight-test-safety",
29
+ "flight-test-operations/envelope/envelope-expansion",
30
+ "flight-test-operations/envelope/v-speeds",
31
+ "flight-test-operations/envelope/load-factor-envelope",
32
+ "flight-test-operations/envelope/stall-characteristics-testing",
33
+ "flight-test-operations/envelope/high-angle-of-attack-testing",
34
+ "flight-test-operations/envelope/buffet-boundary-testing",
35
+ "flight-test-operations/envelope/spin-testing",
36
+ "flight-test-operations/envelope/icing-flight-test",
37
+ "flight-test-operations/envelope/vmc-determination",
38
+ "flight-test-operations/envelope/flight-loads-survey",
39
+ "flight-test-operations/envelope/structural-coupling-test",
40
+ "flight-test-operations/flutter/ground-vibration-testing",
41
+ "flight-test-operations/flutter/flight-vibration-survey",
42
+ "flight-test-operations/flutter/flutter-testing",
43
+ "flight-test-operations/flutter/limit-cycle-oscillation",
44
+ "flight-test-operations/performance/accelerate-stop-distance",
45
+ "flight-test-operations/performance/climb-performance-flight-test",
46
+ "flight-test-operations/performance/cruise-performance-flight-test",
47
+ "flight-test-operations/performance/engine-failure-takeoff-flight-test",
48
+ "flight-test-operations/performance/engine-flight-test"
49
+ ]
50
+
51
+ STAGES = [
52
+ "1. Safety planning",
53
+ "2. Ground vibration",
54
+ "3. Flutter build-up",
55
+ "4. V-speeds",
56
+ "5. Envelope expansion",
57
+ "6. Stall/alpha",
58
+ "7. Buffet/loads",
59
+ "8. Spin/icing",
60
+ "9. Structural coupling",
61
+ "10. Performance",
62
+ "11. Report"
63
+ ]
64
+
65
+
66
+ def role_text():
67
+ p = os.path.join(ROLE_DIR, "ROLE.md")
68
+ return open(p).read() if os.path.exists(p) else None
69
+
70
+
71
+ class TestFlightTestEngineerRole(unittest.TestCase):
72
+
73
+ def test_bound_skills_resolve(self):
74
+ if not HAS_SKILLS:
75
+ self.skipTest("skills checkout absent (cross-repo dev check)")
76
+ for leaf in EXPECTED_BOUND:
77
+ self.assertTrue(
78
+ os.path.exists(os.path.join(AEROSKILLS, "skills", leaf, "SKILL.md")),
79
+ f"bound skill missing: {leaf}")
80
+
81
+ def test_stages_ordered(self):
82
+ role = role_text()
83
+ self.assertIsNotNone(role)
84
+ wf = role[role.find("## Workflow"):role.find("## Evidence gates")]
85
+ idx = [wf.find(s) for s in STAGES]
86
+ self.assertTrue(all(i >= 0 for i in idx), "stage missing in workflow")
87
+ self.assertEqual(idx, sorted(idx), "stage order not deterministic")
88
+
89
+ def test_how_to_run_present(self):
90
+ role = role_text()
91
+ self.assertIsNotNone(role)
92
+ for phrase in ["cli.py build", "cli.py check", "flight_test_core"]:
93
+ self.assertIn(phrase, role, f"missing run instruction: {phrase}")
94
+
95
+ def test_executable_core_and_cli_present(self):
96
+ self.assertTrue(os.path.exists(CORE), "core/flight_test_core.py missing")
97
+ self.assertTrue(os.path.exists(CLI), "cli.py missing")
98
+ # cli references the core engine
99
+ cli = open(CLI).read()
100
+ self.assertIn("flight_test_core", cli)
101
+
102
+ def test_core_runs_standalone(self):
103
+ # core must import and produce a passing model with no skills repo
104
+ old = os.environ.get("AEROSKILLS_DEV")
105
+ os.environ["AEROSKILLS_DEV"] = "/nonexistent"
106
+ try:
107
+ sys.path.insert(0, os.path.join(ROLE_DIR, "core"))
108
+ import flight_test_core as core
109
+ model = core.build_flight_test_deliverable(core.example_aircraft())
110
+ self.assertTrue(core.check_flight_test_deliverable(model)["all_pass"])
111
+ md = core.render_flight_test_markdown(model)
112
+ self.assertTrue(core.check_flight_test_markdown(md)["all_pass"])
113
+ finally:
114
+ if old is None:
115
+ os.environ.pop("AEROSKILLS_DEV", None)
116
+ else:
117
+ os.environ["AEROSKILLS_DEV"] = old
118
+
119
+ def test_template_complete_no_blanks(self):
120
+ self.assertTrue(os.path.exists(TEMPLATE), "deliverable template missing")
121
+ text = open(TEMPLATE).read()
122
+ self.assertNotIn("___", text, "template still has blank placeholders")
123
+ # filled deliverable sections 1..6
124
+ for n in range(1, 7):
125
+ self.assertTrue(re.search(rf"^## {n}\. ", text, re.M),
126
+ f"section {n} missing from template")
127
+ low = text.lower()
128
+ self.assertIn("not a clearance", low)
129
+ self.assertIn("draft", low)
130
+ self.assertIn("pass", low)
131
+ self.assertIn("fail", low)
132
+ # real numbers present (V-speeds, flutter margin, V_D)
133
+ self.assertIn("v_d", low)
134
+ self.assertTrue(re.search(r"\d+\.\d+", text))
135
+
136
+ def test_boundaries(self):
137
+ role = role_text()
138
+ self.assertIsNotNone(role)
139
+ low = role.lower()
140
+ for phrase in ["sign_off_required: true", "forbidden",
141
+ "never clear an envelope", "not a clearance"]:
142
+ self.assertIn(phrase, low, f"missing boundary: {phrase}")
143
+
144
+ def test_sources(self):
145
+ self.assertTrue(os.path.exists(os.path.join(ROLE_DIR, "SOURCES.md")),
146
+ "SOURCES.md missing")
147
+
148
+
149
+ if __name__ == "__main__":
150
+ unittest.main()