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,92 @@
1
+ # Propulsion Design Report
2
+
3
+ **Item:** High-bypass turbofan for a 200-seat twin (design study)
4
+ **Vehicle / mission:** 200-seat twin-aisle-class transport, 2 wing-mounted engines - Subsonic transport cruise design point (M0.78, FL350)
5
+ **Engine class:** high-bypass turbofan (BPR 8, FPR 1.60, OPR 30, TIT 1650 K)
6
+ **Status:** draft-for-review
7
+ **Certification basis:** Design study in a FAR 33 engine-type context; no certification approval claimed
8
+
9
+ ## 1. Requirement and selected cycle
10
+
11
+ - Thrust requirement (per engine, design point): 30.0 kN net at M0.78, 11 km ISA.
12
+ - Selected cycle: separate-stream turbofan with the fan on the low-pressure spool and the core compressor/turbine on the high-pressure spool. Rationale: high bypass ratio (8) lowers mean jet velocity and raises propulsive efficiency at a transonic cruise Mach; the OPR 30 / TIT 1650 K core delivers the cruise SFC of 0.541 lb/(lbf.h) computed below.
13
+ - Design airflow (total, matched to the requirement): 141.0 kg/s.
14
+
15
+ ## 2. Cycle analysis (airbreathing)
16
+
17
+ On-design station state table (total temperature / total pressure, per kg/s inlet airflow basis; component efficiencies are stated inputs below):
18
+
19
+ | Station | Name | Tt (K) | Pt (kPa) |
20
+ |---|---|---|---|
21
+ | 2 | fan face | 245.4 | 35.5 |
22
+ | 13 | fan exit | 284.6 | 56.7 |
23
+ | 3 | compressor exit | 699.1 | 1063.7 |
24
+ | 4 | turbine inlet (TIT) | 1650.0 | 1010.5 |
25
+ | 5 | turbine exit | 996.2 | 97.5 |
26
+
27
+ Core nozzle (station 9): exit static 855 K / 52.7 kPa, jet velocity 565.6 m/s, Mach 1.000, choked, exit area 0.2936 m^2 (per engine).
28
+ Fan nozzle (station 19): exit static 237 K / 29.7 kPa, jet velocity 305.6 m/s, Mach 1.000, choked, exit area 1.1711 m^2 (per engine).
29
+
30
+ - Net thrust (per engine, design point): 30.0 kN = sum of m_dot*(V9 - V0) + (P9 - P0)*A9 over both streams.
31
+ - TSFC: 15.3 g/(kN.s) (0.541 lb/(lbf.h)).
32
+ - Fuel/air ratio (core): 0.02937 (total basis 0.00326); fuel flow 0.460 kg/s per engine.
33
+ - Efficiencies: overall 0.349, thermal 0.412, propulsive 0.848 (kinetic-energy method over both streams); ideal Brayton at OPR 30: 0.622.
34
+
35
+ Input assumptions (stated): fan efficiency 0.90, core compressor efficiency 0.90, turbine 0.90, combustor 0.995 (pressure ratio 0.95), inlet recovery 0.995, fan-duct ratio 0.99, mechanical 0.99, nozzle velocity coefficient 0.99; fuel LHV 43.2 MJ/kg (kerosene class). Component maps and stage counts are representative input.
36
+
37
+ ## 3. Off-design
38
+
39
+ Quick off-design model (turbofan-off-design leaf): corrected mass flow and turbine inlet temperature held at the design value; physical airflow scales by delta/sqrt(theta); net thrust is recomputed through the on-design cycle at each flight point.
40
+
41
+ | Point | Alt (km) | Mach | Throttle | Verdict | m_dot (kg/s) | F_net (kN) | TSFC g/(kN.s) | Surge clearance | Map verdict |
42
+ |---|---|---|---|---|---|---|---|---|---|
43
+ | SLS static | 0 | 0.00 | 1.00 | max-continuous | 370.1 | 111.5 | 9.7 | 15% | on-map |
44
+ | FL250 climb | 8 | 0.78 | 1.00 | max-continuous | 213.0 | 39.4 | 16.7 | 15% | on-map |
45
+ | FL350 design cruise | 11 | 0.78 | 1.00 | max-continuous | 141.0 | 30.0 | 15.3 | 15% | on-map |
46
+ | FL350 part power | 11 | 0.78 | 0.60 | cruise | 141.0 | 18.0 | 15.3 | 15% | on-map |
47
+ | FL390 top of cruise | 12 | 0.80 | 1.00 | max-continuous | 119.0 | 25.7 | 15.1 | 15% | on-map |
48
+
49
+ Surge clearance: representative core-compressor surge line set 115% above the operating line pressure ratio (34 vs OPR 30); the operating line clears surge at every envelope point (representative map input, not vendor data).
50
+
51
+ ## 4. Component selection
52
+
53
+ | Component | Choice | Basis |
54
+ |---|---|---|
55
+ | Inlet | subsonic pitot, recovery 0.995 | cruise M0.78 |
56
+ | Fan | single stage, PR 1.60, eta 0.90 | Tt 285 K at design |
57
+ | Core compressor | multi-stage axial, PR 18.8 (OPR 30/fan 1.60) | Tt 699 K, representative map margin |
58
+ | Combustor | annular, eta 0.995, pressure ratio 0.95 | f/a 0.02937 |
59
+ | HP turbine | cooled (turbine-blade-cooling stage), eta 0.90 | Tt in 1650 K, Tt out 996 K |
60
+ | Core nozzle | convergent, choked at design | area 0.2936 m^2 |
61
+ | Fan nozzle | convergent, choked at design | area 1.1711 m^2 |
62
+
63
+ ## 5. Integration
64
+
65
+ - Ram-drag bookkeeping (engine-airframe integration): net thrust above already subtracts the inlet momentum drag m_dot_0*V0 (= 32.6 kN at the design point).
66
+ - Installed performance: nacelle, pylon and bleed/accessory losses are NOT yet deducted; the uninstalled net thrust is 30.0 kN per engine. Installation bookkeeping is a follow-on engine-airframe integration pass.
67
+
68
+ ## 6. Rocket / space option (if applicable)
69
+
70
+ Not applicable to this airbreathing transport item - the engine selection is turbofan. For a space item the rocket-engine-cycle and nozzle-design stages apply (see the rocket reference example in the core / cli build --engine rocket).
71
+
72
+ ## 7. Electric option (if applicable)
73
+
74
+ Not applicable: no electric-propulsion role for a 30 kN cruise thrust requirement at M0.78. Electric thrusters cover the low-thrust space regime (hall/gridded-ion), not airbreathing main propulsion.
75
+
76
+ ## 8. Bypass-ratio trade and recommendation
77
+
78
+ First-order trade at fixed core conditions, fixed total mass flow and jet velocities held at the design-point values (bypass-ratio-trade leaf method):
79
+
80
+ | BPR | F/m_dot (N per kg/s) | TSFC g/(kN.s) | eta_o |
81
+ |---|---|---|---|
82
+ | 2 | 273.3 | 35.8 | 0.149 |
83
+ | 4 | 211.0 | 27.8 | 0.192 |
84
+ | 6 | 184.3 | 22.8 | 0.235 |
85
+ | 8 | 169.5 | 19.3 | 0.278 |
86
+ | 10 | 160.1 | 16.7 | 0.321 |
87
+ | 12 | 153.5 | 14.7 | 0.364 |
88
+
89
+ Raising BPR lowers specific thrust and TSFC by shifting flow to the fan stream (bypass-ratio-trade trend), at the cost of fan diameter and nacelle drag. BPR 8 balances specific thrust with nacelle integration for a 200-seat twin; the design point above is the recommendation. Open items: nacelle drag, turbine cooling flow, and the component map verification.
90
+
91
+ ---
92
+ *Generated by Aero Agent Roles propulsion-engineer core (2026-09-05). DRAFT for human propulsion lead review. Not an approval document.*
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env python3
2
+ """Test propulsion_core: the executable engine of the Propulsion Engineer.
3
+
4
+ Proves the role can DO its job standalone (no AeroSkills needed): ISA,
5
+ Brayton/real-cycle relations, turbofan on-design thrust/TSFC/efficiency,
6
+ nozzle choked/unchoked logic, compressor-map surge verdicts, off-design
7
+ envelope, bypass-ratio trade, rocket Isp/mass-split/cycle feasibility,
8
+ report builder + evidence gates, and standalone mode.
9
+ """
10
+ import os
11
+ import re
12
+ import sys
13
+ import unittest
14
+
15
+ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
16
+ "..", "core"))
17
+ from propulsion_core import ( # noqa: E402
18
+ PROPELLANTS, bpr_trend, build_report, build_rocket_report,
19
+ build_turbofan_report, brayton_thermal_efficiency, check_report,
20
+ check_report_markdown, compressor_exit_temperature,
21
+ cycle_feasibility, example_rocket_item, example_rocket_markdown,
22
+ example_turbofan_item, example_turbofan_markdown, exit_mach_from_area_ratio,
23
+ isa, map_verdict, mass_flow_split, off_design_envelope, operating_line_clearance,
24
+ real_thermal_efficiency, render_report_markdown, rocket_mass_flow,
25
+ rocket_nozzle_sizing, thrust_split, turbofan_design_point,
26
+ turbine_exit_temperature, turbine_power,
27
+ )
28
+
29
+ # Deterministic across midnight: pin fresh renders to the date the
30
+ # committed template carries (CI override wins).
31
+ os.environ.setdefault("ROLE_GEN_DATE", "2026-09-05")
32
+
33
+
34
+ class TestCoreDomainRules(unittest.TestCase):
35
+ """Public-standard anchors mirrored from the bound AeroSkills leaves."""
36
+
37
+ def test_isa_at_sea_level(self):
38
+ s = isa(0.0)
39
+ self.assertAlmostEqual(s["t"], 288.15, places=2)
40
+ self.assertAlmostEqual(s["p"], 101325.0, places=1)
41
+
42
+ def test_brayton_anchor_pr8(self):
43
+ # Ideal Brayton at PR 8, gamma 1.4: 0.44795 (leaf anchor).
44
+ self.assertAlmostEqual(brayton_thermal_efficiency(8.0), 0.44795,
45
+ delta=1e-3)
46
+
47
+ def test_compressor_anchor(self):
48
+ # T2 = T1*PR^((g-1)/g), 288 K / PR 8: 521.70 K (leaf anchor).
49
+ self.assertAlmostEqual(compressor_exit_temperature(288.0, 8.0),
50
+ 521.70, delta=0.1)
51
+ # real-cycle with efficiency raises the exit temperature
52
+ self.assertGreater(
53
+ compressor_exit_temperature(288.0, 8.0, eta_c=0.85),
54
+ compressor_exit_temperature(288.0, 8.0, eta_c=1.0))
55
+
56
+ def test_turbine_anchor(self):
57
+ # T4 = T3/PR^((g-1)/g), 1400 K / PR 8: 772.86 K (leaf anchor).
58
+ self.assertAlmostEqual(turbine_exit_temperature(1400.0, 8.0, 1.4),
59
+ 772.86, delta=0.1)
60
+
61
+ def test_real_thermal_efficiency_order(self):
62
+ # Requires physically ordered stations; net/heat < 1.
63
+ eta = real_thermal_efficiency(288.0, 600.0, 1500.0, 900.0)
64
+ self.assertGreater(eta, 0.0)
65
+ self.assertLess(eta, 1.0)
66
+ with self.assertRaises(ValueError):
67
+ real_thermal_efficiency(600.0, 288.0, 1500.0, 900.0)
68
+
69
+ def test_area_mach_supersonic(self):
70
+ # area ratio 2 -> Mach ~1.5-2 on the supersonic branch (gamma 1.2)
71
+ m = exit_mach_from_area_ratio(2.0, 1.2)
72
+ self.assertGreater(m, 1.0)
73
+ with self.assertRaises(ValueError):
74
+ exit_mach_from_area_ratio(1.0, 1.2) # sonic is not supersonic
75
+
76
+
77
+ class TestTurbofanDesignPoint(unittest.TestCase):
78
+ """On-design cycle: real numbers, physical ordering, gates."""
79
+
80
+ def test_cruise_design_point_realistic(self):
81
+ # Reference item: BPR 8 / OPR 30 / TIT 1650 at M0.78 FL350.
82
+ d = turbofan_design_point(0.78, 10668.0, 8.0, 1.6, 30.0, 1650.0)
83
+ # station temperature ordering
84
+ st = d["stations"]
85
+ self.assertLess(st["2"]["tt"], st["13"]["tt"])
86
+ self.assertLess(st["13"]["tt"], st["3"]["tt"])
87
+ self.assertLess(st["3"]["tt"], st["4"]["tt"])
88
+ self.assertLess(st["5"]["tt"], st["4"]["tt"])
89
+ # realistic compressor exit for OPR 30 at cruise (700 K ballpark)
90
+ self.assertTrue(600 < st["3"]["tt"] < 850, st["3"]["tt"])
91
+ # net thrust per kg/s air positive and in a physical band (N)
92
+ self.assertTrue(150 < d["f_net_per_kg"] < 250, d["f_net_per_kg"])
93
+ # cruise TSFC in the modern high-bypass band: 12-20 g/(kN s)
94
+ tsfc = d["tsfc_kg_Ns"] * 1e6
95
+ self.assertTrue(12.0 < tsfc < 20.0, tsfc)
96
+ # efficiencies bounded and consistent: eta_o = eta_th * eta_p
97
+ self.assertLess(d["eta_o"], 1.0)
98
+ self.assertLess(d["eta_p"], 1.0)
99
+ self.assertAlmostEqual(d["eta_o"], d["eta_th"] * d["eta_p"], delta=1e-6)
100
+
101
+ def test_scaled_to_thrust_requirement(self):
102
+ item = example_turbofan_item()
103
+ model = build_turbofan_report(item)
104
+ d = model["design"]
105
+ # sizing hits the requirement
106
+ self.assertAlmostEqual(d["f_net"], 30000.0, delta=100.0)
107
+ self.assertGreater(d["mdot0"], 50.0) # 100+ kg/s class airflow
108
+ self.assertGreater(d["mdot_fuel"], 0.3) # kg/s fuel flow
109
+
110
+ def test_sls_static_nozzles_unchoked(self):
111
+ # at SLS (M0, sea level) the nozzle pressure ratios are below the
112
+ # critical ratio (fan NPR ~1.6 < 1.89), so both are unchoked and
113
+ # fully expanded to ambient.
114
+ d = turbofan_design_point(0.0, 0.0, 8.0, 1.6, 30.0, 1650.0)
115
+ self.assertFalse(d["nozzle_core"]["choked"])
116
+ self.assertFalse(d["nozzle_fan"]["choked"])
117
+ self.assertEqual(d["v0"], 0.0)
118
+ # at the M0.78 / FL350 design point both nozzles are choked
119
+ c = turbofan_design_point(0.78, 10668.0, 8.0, 1.6, 30.0, 1650.0)
120
+ self.assertTrue(c["nozzle_core"]["choked"])
121
+ self.assertTrue(c["nozzle_fan"]["choked"])
122
+
123
+ def test_bad_inputs_raise(self):
124
+ with self.assertRaises(ValueError):
125
+ turbofan_design_point(1.2, 0.0, 8.0, 1.6, 30.0, 1650.0)
126
+ with self.assertRaises(ValueError):
127
+ turbofan_design_point(0.78, 10668.0, 0.0, 1.6, 30.0, 1650.0)
128
+ with self.assertRaises(ValueError):
129
+ turbofan_design_point(0.78, 10668.0, 8.0, 1.0, 30.0, 1650.0)
130
+ with self.assertRaises(ValueError):
131
+ turbofan_design_point(0.78, 10668.0, 8.0, 1.6, 30.0, 1400.0)
132
+
133
+ def test_thrust_identity(self):
134
+ # momentum+pressure form reproduces thrust: F = mdot(V9-V0)+(P9-P0)A9
135
+ d = turbofan_design_point(0.78, 10668.0, 8.0, 1.6, 30.0, 1650.0)
136
+ nc, nf = d["nozzle_core"], d["nozzle_fan"]
137
+ f = (d["mdot_core_frac"] * (1 + d["f_air_core"]) * (nc["ve"] - d["v0"])
138
+ + (nc["pe"] - d["p0"]) * nc["a_exit"]
139
+ + d["mdot_fan_frac"] * (nf["ve"] - d["v0"])
140
+ + (nf["pe"] - d["p0"]) * nf["a_exit"])
141
+ self.assertAlmostEqual(f, d["f_net_per_kg"], delta=1e-6)
142
+
143
+
144
+ class TestOffDesignAndTrade(unittest.TestCase):
145
+ def test_off_design_envelope_covers_points(self):
146
+ d = turbofan_design_point(0.78, 10668.0, 8.0, 1.6, 30.0, 1650.0)
147
+ d["mdot0"] = 100.0
148
+ pts = [("SLS", 0.0, 0.0, 1.0), ("FL250", 7620.0, 0.78, 1.0),
149
+ ("part power", 10668.0, 0.78, 0.6)]
150
+ rows = off_design_envelope(d, pts)
151
+ self.assertEqual(len(rows), 3)
152
+ for r in rows:
153
+ self.assertGreater(r["mdot_phys"], 0.0)
154
+ self.assertGreater(r["f_net"], 0.0)
155
+ self.assertIn(r["map_verdict"], ("on-map", "approaching-surge"))
156
+ self.assertTrue(r["surge_clearance_pct"] > 0.0)
157
+ # named point carried through, part-power below the max row
158
+ self.assertEqual(rows[0]["name"], "SLS")
159
+ self.assertLess(rows[2]["f_throttled"], rows[2]["f_net"])
160
+
161
+ def test_surge_clearance_and_verdict(self):
162
+ # clearance positive below surge; verdict on-map until within 5%
163
+ self.assertAlmostEqual(operating_line_clearance(20.0, 23.0), 15.0)
164
+ self.assertEqual(map_verdict(20.0, 23.0), "on-map")
165
+ self.assertEqual(map_verdict(22.4, 23.0), "approaching-surge")
166
+ with self.assertRaises(ValueError):
167
+ map_verdict(23.0, 23.0) # on/above the surge line is rejected
168
+
169
+ def test_thrust_split_and_bpr_trend(self):
170
+ s = thrust_split(8.0, 90.0, 500.0, 300.0, 231.0)
171
+ self.assertGreater(s["f_total"], 0.0)
172
+ # trend signature: bpr_trend(mdot_total, vj_core, vj_fan, v0, f_core)
173
+ trend = bpr_trend(90.0, 500.0, 300.0, 231.0, 0.02)
174
+ self.assertEqual(len(trend), 6)
175
+ # fixed core/fan jet velocities, fixed total flow (leaf model):
176
+ # higher BPR shifts flow to the slower fan stream -> specific
177
+ # thrust and TSFC both fall monotonically.
178
+ specs = [r["specific_thrust"] for r in trend]
179
+ tsfcs = [r["tsfc_g_kN_s"] for r in trend]
180
+ self.assertEqual(specs, sorted(specs, reverse=True))
181
+ self.assertEqual(tsfcs, sorted(tsfcs, reverse=True))
182
+
183
+
184
+ class TestRocketDomain(unittest.TestCase):
185
+ def test_propellant_table(self):
186
+ # reference-typical values from the rocket-engine-cycle leaf
187
+ self.assertEqual(PROPELLANTS["LOX/LH2"], (1140.0, 71.0, 5.5, 430.0))
188
+ self.assertEqual(PROPELLANTS["LOX/RP-1"], (1140.0, 820.0, 2.56, 300.0))
189
+
190
+ def test_mass_flow_from_isp(self):
191
+ # mdot = F/(Isp*g0): 110 kN at Isp 430 -> ~26.1 kg/s
192
+ m = rocket_mass_flow(110000.0, 430.0)
193
+ self.assertAlmostEqual(m, 26.1, delta=0.2)
194
+ mdot, mox, mf = mass_flow_split(110000.0, 430.0, 5.5)
195
+ self.assertAlmostEqual(mdot, mox + mf, delta=1e-9)
196
+ self.assertAlmostEqual(mox / mf, 5.5, delta=1e-9)
197
+
198
+ def test_cycle_feasibility_bounds(self):
199
+ # pressure-fed bound at 3 MPa; expander LOX/LH2 <= 10 MPa
200
+ ok, _ = cycle_feasibility("pressure-fed", "LOX/LH2", 2.0e6)
201
+ self.assertTrue(ok)
202
+ ok, _ = cycle_feasibility("pressure-fed", "LOX/LH2", 4.0e6)
203
+ self.assertFalse(ok)
204
+ ok, _ = cycle_feasibility("expander", "LOX/LH2", 8.0e6)
205
+ self.assertTrue(ok)
206
+ ok, _ = cycle_feasibility("expander", "LOX/RP-1", 4.0e6)
207
+ self.assertFalse(ok) # expander needs the LH2 fuel
208
+ ok, _ = cycle_feasibility("staged-combustion", "LOX/RP-1", 20.0e6)
209
+ self.assertTrue(ok)
210
+
211
+ def test_turbine_power_positive(self):
212
+ p = turbine_power(4.0, 2000.0, 1200.0, 0.6, 1.2, 3.2e6, 0.2e6)
213
+ self.assertGreater(p, 0.0)
214
+
215
+ def test_nozzle_sizing(self):
216
+ n = rocket_nozzle_sizing(26.1, 4.0e6, 3500.0, 1.2, 84.0, r_gas=693.0)
217
+ self.assertGreater(n["a_star"], 0.0)
218
+ self.assertGreater(n["me"], 3.0) # supersonic, high area ratio
219
+ self.assertLess(n["pe"], 4.0e6)
220
+ self.assertGreater(n["cf_ideal"], 1.5) # vacuum Cf of a good nozzle
221
+ # ideal exit velocity implies ideal vacuum Isp > real typical
222
+ self.assertGreater(n["ve"] / 9.80665, 430.0)
223
+
224
+ def test_rocket_report_builder(self):
225
+ model = build_rocket_report(example_rocket_item())
226
+ self.assertEqual(model["engine_kind"], "rocket")
227
+ self.assertEqual(model["cycle"]["propellant"], "LOX/LH2")
228
+ self.assertTrue(model["cycle"]["feasible"])
229
+ gates = check_report(model)
230
+ self.assertTrue(gates["all_pass"], gates)
231
+ md = render_report_markdown(model)
232
+ self.assertIn("not an approval", md.lower())
233
+
234
+
235
+ class TestReportBuilderAndGates(unittest.TestCase):
236
+ def test_turbofan_report_gates(self):
237
+ model = build_report(example_turbofan_item())
238
+ self.assertEqual(model["document_type"], "Propulsion Design Report")
239
+ self.assertEqual(model["status"], "draft-for-review")
240
+ gates = check_report(model)
241
+ self.assertTrue(gates["all_pass"], gates)
242
+
243
+ def test_markdown_gates(self):
244
+ md = example_turbofan_markdown()
245
+ gates = check_report_markdown(md)
246
+ self.assertTrue(gates["all_pass"], gates)
247
+ for section in ("## 1. Requirement", "## 2. Cycle analysis",
248
+ "## 3. Off-design", "## 4. Component selection",
249
+ "## 8. Bypass-ratio trade"):
250
+ self.assertIn(section, md)
251
+ self.assertIn("not an approval", md.lower())
252
+
253
+ def test_report_has_computed_numbers(self):
254
+ # evidence gate: every headline performance claim is a number
255
+ md = example_turbofan_markdown()
256
+ self.assertRegex(md, r"kN")
257
+ self.assertRegex(md, r"g/\(kN\.s\)|lb/\(lbf\.h\)")
258
+ self.assertRegex(md, r"\d+\.\d+ kg/s")
259
+ self.assertRegex(md, r"K")
260
+
261
+ def test_rocket_markdown_no_blank_template(self):
262
+ md = example_rocket_markdown()
263
+ self.assertNotIn("___", md)
264
+ self.assertIn("not an approval", md.lower())
265
+
266
+ def test_deliverable_template_filled(self):
267
+ # templates/propulsion-report-template.md is the FILLED example
268
+ # deliverable (the reference turbofan item): zero blanks, and it
269
+ # matches what the core renders today (modulo the date footer).
270
+ here = os.path.dirname(os.path.abspath(__file__))
271
+ tmpl = os.path.join(here, "..", "templates",
272
+ "propulsion-report-template.md")
273
+ with open(tmpl) as f:
274
+ text = f.read()
275
+ self.assertNotIn("___", text)
276
+ low = text.lower()
277
+ self.assertIn("not an approval", low)
278
+ self.assertIn("draft", low)
279
+ fresh = example_turbofan_markdown()
280
+ norm = lambda s: re.sub(r"\(20\d\d-\d\d-\d\d\)", "(DATE)", s)
281
+ self.assertEqual(norm(text), norm(fresh))
282
+
283
+ def test_standalone_no_skills_repo(self):
284
+ os.environ["AEROSKILLS_DEV"] = "/nonexistent"
285
+ for item, render in ((example_turbofan_item(), example_turbofan_markdown),
286
+ (example_rocket_item(), example_rocket_markdown)):
287
+ model = build_report(item)
288
+ self.assertTrue(check_report(model)["all_pass"])
289
+ md = render()
290
+ self.assertGreater(len(md), 1000)
291
+
292
+
293
+ if __name__ == "__main__":
294
+ unittest.main(verbosity=2)
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env python3
2
+ """Role test: propulsion-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", "propulsion-engineer")
12
+ TEMPLATE = os.path.join(ROLE_DIR, "templates", "propulsion-report-template.md")
13
+
14
+ EXPECTED_BOUND = [
15
+ "propulsion/gas-turbine-cycle/gas-turbine-cycle",
16
+ "propulsion/gas-turbine-cycle/real-cycle-effects",
17
+ "propulsion/gas-turbine-cycle/afterburner-cycle",
18
+ "propulsion/gas-turbine-cycle/regenerative-cycle",
19
+ "propulsion/gas-turbine-cycle/propelling-nozzle",
20
+ "propulsion/gas-turbine-cycle/subsonic-inlet-recovery",
21
+ "propulsion/gas-turbine-cycle/combustor-design",
22
+ "propulsion/turbofan/turbofan-cycle",
23
+ "propulsion/turbofan/bypass-ratio-trade",
24
+ "propulsion/turbofan/turbofan-off-design",
25
+ "propulsion/turboprop/turboprop-cycle",
26
+ "propulsion/turboprop/free-turbine",
27
+ "propulsion/axial-compressor/axial-compressor-stage",
28
+ "propulsion/axial-compressor/compressor-map",
29
+ "propulsion/axial-compressor/multi-stage-compressor",
30
+ "propulsion/axial-compressor/turbine-stage",
31
+ "propulsion/axial-compressor/turbine-blade-cooling",
32
+ "propulsion/engine-airframe/engine-airframe-integration",
33
+ "propulsion/rocket/rocket-engine-cycle",
34
+ "propulsion/rocket/nozzle-design",
35
+ "propulsion/rocket/combustion-chamber-design",
36
+ "propulsion/rocket/injector-design",
37
+ "propulsion/rocket/propellant-selection",
38
+ "propulsion/rocket/solid-rocket-motor",
39
+ "propulsion/rocket/hybrid-rocket-motor",
40
+ "propulsion/rocket/thrust-chamber-cooling",
41
+ "propulsion/rocket/thrust-vector-control",
42
+ "propulsion/rocket/cold-gas-thruster",
43
+ "propulsion/combustion/cea-rocket-combustion",
44
+ "propulsion/electric/hall-thruster",
45
+ "propulsion/electric/gridded-ion-thruster",
46
+ "propulsion/electric/electrothermal-thruster",
47
+ "propulsion/ramjet/ramjet-cycle",
48
+ "propulsion/ramjet/ramjet-inlet"
49
+ ]
50
+
51
+ STAGES = [
52
+ "1. Airbreathing cycle",
53
+ "2. Bypass/regenerative/AB trades",
54
+ "3. Off-design",
55
+ "4. Components",
56
+ "5. Turbine cooling",
57
+ "6. Integration",
58
+ "7. Rocket cycle",
59
+ "8. Solid/hybrid",
60
+ "9. Cooling/TVC",
61
+ "10. Combustion",
62
+ "11. Electric",
63
+ "12. Ramjet",
64
+ "13. Report"
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 TestPropulsionEngineerRole(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,161 @@
1
+ ---
2
+ type: role
3
+ name: space-systems-engineer
4
+ title: "Space Systems Engineer"
5
+ status: draft
6
+ domain: space-systems
7
+ deliverable_type: "spacecraft mission + subsystem design report"
8
+ standards_bound:
9
+ - id: ecss
10
+ tier: TIER-2
11
+ reference-only: true
12
+ skills_bound:
13
+ - space-systems/mission-design/mission-delta-v-budget
14
+ - space-systems/mission-design/launch-window-analysis
15
+ - space-systems/mission-design/c3-departure-energy
16
+ - space-systems/mission-design/entry-descent-landing
17
+ - space-systems/mission-design/radiation-debris
18
+ - space-systems/orbit-mechanics/keplerian-elements
19
+ - space-systems/orbit-mechanics/kepler-orbit-propagation
20
+ - space-systems/orbit-mechanics/hohmann-transfer
21
+ - space-systems/orbit-mechanics/bi-elliptic-transfer
22
+ - space-systems/orbit-mechanics/plane-change-maneuver
23
+ - space-systems/orbit-mechanics/lambert-transfer
24
+ - space-systems/orbit-mechanics/gravity-assist-swingby
25
+ - space-systems/orbit-mechanics/low-thrust-spiral
26
+ - space-systems/orbit-mechanics/orbital-perturbations
27
+ - space-systems/orbit-mechanics/sun-synchronous-inclination
28
+ - space-systems/orbit-mechanics/ground-track-repeat
29
+ - space-systems/orbit-mechanics/eclipse-time
30
+ - space-systems/orbit-mechanics/satellite-coverage
31
+ - space-systems/orbit-mechanics/walker-delta-constellation
32
+ - space-systems/orbit-mechanics/geostationary-station-keeping
33
+ - space-systems/orbit-mechanics/conjunction-assessment
34
+ - space-systems/orbit-mechanics/three-body-libration
35
+ - space-systems/orbit-mechanics/orbital-decay
36
+ - space-systems/orbit-mechanics/clohessy-wiltshire
37
+ - space-systems/adcs/attitude-determination-triad
38
+ - space-systems/adcs/attitude-determination-quest
39
+ - space-systems/adcs/star-tracker
40
+ - space-systems/adcs/sun-pointing
41
+ - space-systems/adcs/reaction-wheel-control
42
+ - space-systems/adcs/control-moment-gyro
43
+ - space-systems/adcs/magnetorquer-control
44
+ - space-systems/adcs/attitude-control-sizing
45
+ - space-systems/adcs/pointing-error-budget
46
+ - space-systems/adcs/gyro-allan-variance
47
+ - space-systems/subsystems/communication-link-budget
48
+ - space-systems/subsystems/antenna-aperture-sizing
49
+ - space-systems/subsystems/power-thermal-budget
50
+ - space-systems/subsystems/solar-array-sizing
51
+ - space-systems/subsystems/spacecraft-battery-sizing
52
+ - space-systems/subsystems/thermal-design
53
+ - space-systems/subsystems/propellant-tank-sizing
54
+ - space-systems/subsystems/command-data-handling
55
+ - space-systems/ecss/systems-engineering
56
+ - space-systems/ecss/software-engineering
57
+ - space-systems/ecss/software-verification
58
+ tools_allowed: [stdlib, offline-file-processing]
59
+ forbidden:
60
+ - "claim certification approval"
61
+ - "declare launch readiness"
62
+ - "reproduce proprietary standard text"
63
+ sign_off_required: true
64
+ license: Apache-2.0
65
+ compatibility: "agentskills.io ROLE.md; any SKILL.md host"
66
+ metadata:
67
+ version: 0.1.0
68
+ author: Aero Agent Roles
69
+ skills_release: "v1.3.0+"
70
+ ---
71
+
72
+ # Space Systems Engineer
73
+
74
+ ## Role identity
75
+
76
+ Owns spacecraft mission and subsystem design: delta-v budget, transfer
77
+ and maneuver design, constellation/coverage analysis, ADCS selection,
78
+ and the subsystem sizing loop (power, thermal, comms, propulsion,
79
+ C&DH) that closes a satellite design against its mission. Use when a
80
+ space mission needs a defensible spacecraft with a delta-v budget,
81
+ orbit, attitude control solution, and subsystem power/thermal/comm
82
+ budgets. Do NOT use for aircraft certification or airframe roles.
83
+
84
+ ## Deliverable contract
85
+
86
+ 1. **Mission design report** - orbit + maneuver plan, delta-v budget,
87
+ launch window, coverage/constellation result, radiation/debris notes.
88
+ 2. **Subsystem design report** - ADCS selection + pointing budget,
89
+ power/thermal/comm budgets, solar array/battery/tank sizing, C&DH.
90
+ 3. **System budget summary** - mass, power, delta-v, pointing.
91
+
92
+ ## Workflow (stages -> bound skills)
93
+
94
+ | Stage | Loads skill | Produces |
95
+ |---|---|---|
96
+ | 1. Mission delta-v | mission-delta-v-budget + keplerian-elements + kepler-orbit-propagation | orbit elements, total delta-v |
97
+ | 2. Transfers | hohmann-transfer + bi-elliptic-transfer + plane-change-maneuver + lambert-transfer + gravity-assist-swingby + low-thrust-spiral | transfer solution |
98
+ | 3. Launch window | launch-window-analysis + c3-departure-energy | window/C3 |
99
+ | 4. Constellation/coverage | satellite-coverage + walker-delta-constellation + ground-track-repeat + sun-synchronous-inclination + geostationary-station-keeping | coverage result |
100
+ | 5. Environment | eclipse-time + orbital-perturbations + conjunction-assessment + radiation-debris + orbital-decay + three-body-libration | environment inputs |
101
+ | 6. Rendezvous | clohessy-wiltshire | proximity solution |
102
+ | 7. ADCS determination | attitude-determination-triad + attitude-determination-quest + star-tracker + sun-pointing + gyro-allan-variance | attitude knowledge |
103
+ | 8. ADCS control | reaction-wheel-control + control-moment-gyro + magnetorquer-control + attitude-control-sizing + pointing-error-budget | ADCS solution + pointing |
104
+ | 9. Power/thermal | power-thermal-budget + solar-array-sizing + spacecraft-battery-sizing + thermal-design | power/thermal budgets |
105
+ | 10. Comms | communication-link-budget + antenna-aperture-sizing | link budget |
106
+ | 11. Propulsion/C&DH | propellant-tank-sizing + command-data-handling | propulsion sizing, C&DH |
107
+ | 12. ECSS context | ecss/systems-engineering + ecss/software-engineering + ecss/software-verification | process + software context |
108
+ | 13. Report | (all above) | mission + subsystem reports |
109
+
110
+ ## Evidence gates
111
+
112
+ - Stage 1 done = delta-v budget sums with margins; elements consistent
113
+ with the mission.
114
+ - Stage 8 done = pointing error budget meets the requirement.
115
+ - Stage 9 done = power budget closes with eclipse sizing; thermal
116
+ balance stated.
117
+ - FINAL = every budget closes with a stated margin policy.
118
+
119
+ ## Boundary / forbidden
120
+
121
+ - NEVER claim certification approval or declare launch readiness.
122
+ - ECSS is referenced (TIER-2), never reproduced.
123
+ - Subsystem performance assumes the stated models and margins.
124
+
125
+ ## Verification
126
+
127
+ The role runs STANDALONE: `core/space_systems_core.py` is an executable
128
+ engine that computes the orbit elements, delta-v budget (Hohmann and
129
+ disposal legs), propellant mass (rocket equation), reaction wheel sizing
130
+ (h = J*omega), pointing error budget, battery and solar array sizing,
131
+ radiator area, and the comms link budget, then BUILDS the mission +
132
+ subsystem design report and gate-checks deliverables. No AeroSkills
133
+ checkout required.
134
+
135
+ Run the role:
136
+ ```bash
137
+ python3 cli.py build --out report.md # example mission
138
+ python3 cli.py build --out leo550.md --altitude 550 # variant orbit
139
+ python3 cli.py build --out heavy.md --dry-mass 400 --isp 250
140
+ python3 cli.py check --file report.md # gate-check a report
141
+ ```
142
+
143
+ Tests:
144
+ - tests/test_space_systems_engineer_core.py (28 tests): domain rules
145
+ with AeroSkills leaf anchor values + report builder + gates +
146
+ standalone (no skills repo).
147
+ - tests/test_role_space_systems_engineer.py: bound-skill resolution
148
+ (skips if the skills repo is absent) + workflow + template present +
149
+ boundaries.
150
+
151
+ Bound skills in Aero Skills deepen individual stages when the library
152
+ is present (kepler propagation, transfer trade-offs, pointing error
153
+ refinement, detailed tank design); the core engine does not depend on
154
+ them. The worked example in `templates/space-systems-template.md` is the
155
+ generated output of the core for the example mission - REAL numbers, no
156
+ blanks.
157
+
158
+ ## Compliance
159
+
160
+ - ecss TIER-2 reference-only. References summary-not-copy.
161
+ - SOURCES.md records standards referenced.