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,282 @@
1
+ #!/usr/bin/env python3
2
+ """cli.py - run the High-Speed Aerodynamics Engineer role.
3
+
4
+ Usage:
5
+ python3 cli.py build [--out file.md] [--mach M] [--altitude m]
6
+ [--bundle] [--no-dispatch] [--profile p.json]
7
+ # build the High-Speed Aerodynamic Analysis Memo
8
+ # --bundle also emit evidence/{model,gates,provenance}.json
9
+ # --profile apply a customer/program profile (basis, regs, formats)
10
+ python3 cli.py check --file file.md # gate-check an existing memo
11
+
12
+ The role ENGINE (core/high_speed_aero_core.py) does the work standalone:
13
+ isentropic relations, Mach-area, Prandtl-Glauert / Karman-Tsien
14
+ corrections, normal/oblique shock relations, Prandtl-Meyer expansion,
15
+ shock-expansion airfoil, Sears-Haack wave drag, boundary-layer
16
+ thickness/transition, stagnation-line layer, rough-wall skin friction,
17
+ Sutton-Graves heating screen, bow-shock standoff, and the evidence
18
+ gates. When AeroSkills is present (AEROSKILLS_DEV or ~/AeroSkills), the
19
+ CLI dispatches the bound leaf logic (isentropic ratios, normal shock,
20
+ oblique shock, boundary layer) and cross-checks each against the
21
+ core's - two independent implementations agreeing is recorded in
22
+ provenance.json.
23
+ """
24
+ import argparse
25
+ import importlib.util
26
+ import json
27
+ import os
28
+ import sys
29
+
30
+ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
31
+ "core"))
32
+ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
33
+ "..", "..", "scripts"))
34
+ import high_speed_aero_core as core # noqa: E402
35
+ import evidence # noqa: E402
36
+
37
+ AEROSKILLS = os.environ.get("AEROSKILLS_DEV", os.path.expanduser("~/AeroSkills"))
38
+ ROLE_SLUG = "high-speed-aerodynamics-engineer"
39
+ SKILLS_RELEASE = "v1.3.0+"
40
+
41
+ # Dispatch rows: each cross-checks one core quantity against the logic of
42
+ # a bound AeroSkills leaf (same inputs, same formulas - two independent
43
+ # implementations agreeing is the strongest offline evidence available).
44
+ DISPATCH = [
45
+ {"leaf": "aerodynamics/high-speed/isentropic-flow-relations",
46
+ "logic": "isentropic_flow_relations_logic.py",
47
+ "fn": "total_static_ratios", "key": "p0_over_p",
48
+ "args": {"mach": 2.0}, "tol": 1e-9,
49
+ "point": "isentropic p0/p at M 2.0"},
50
+ {"leaf": "aerodynamics/high-speed/normal-shock",
51
+ "logic": "normal_shock_logic.py",
52
+ "fn": "shock_properties", "key": "p02_p01",
53
+ "args": {"M1": 2.0}, "tol": 1e-9,
54
+ "point": "normal-shock total-pressure recovery at M1 = 2.0"},
55
+ {"leaf": "aerodynamics/high-speed/oblique-shock",
56
+ "logic": "oblique_shock_logic.py",
57
+ "fn": "shock_properties", "key": "beta_deg",
58
+ "args": {"M1": 2.0, "theta_deg": 10.0}, "tol": 1e-6,
59
+ "point": "weak oblique-shock wave angle at M1 = 2.0, theta = 10 deg"},
60
+ {"leaf": "aerodynamics/boundary-layer/boundary-layer-theory",
61
+ "logic": "boundary_layer_theory_logic.py",
62
+ "fn": "flat_plate_thicknesses", "key": "delta",
63
+ "args": {"x": 6.0, "re_x": 2.866e7}, "tol": 1e-9,
64
+ "point": "flat-plate BL thickness at x = 6 m, Re_x = 2.866e7"},
65
+ ]
66
+
67
+
68
+ def _core_value(row):
69
+ """Recompute the core value for a dispatch row with the same args."""
70
+ if row["leaf"].endswith("isentropic-flow-relations"):
71
+ return core.total_static_ratios(row["args"]["mach"])[row["key"]]
72
+ if row["leaf"].endswith("normal-shock"):
73
+ return core.normal_shock(row["args"]["M1"])[row["key"]]
74
+ if row["leaf"].endswith("oblique-shock"):
75
+ return core.oblique_shock(row["args"]["M1"],
76
+ row["args"]["theta_deg"])[row["key"]]
77
+ if row["leaf"].endswith("boundary-layer-theory"):
78
+ return core.flat_plate_thicknesses(row["args"]["x"],
79
+ row["args"]["re_x"])[row["key"]]
80
+ return None
81
+
82
+
83
+ def _dispatch_crosschecks(item, model):
84
+ """Run the bound AeroSkills logic files and compare with the core.
85
+
86
+ Returns a list of provenance 'skills' rows (empty when AeroSkills is
87
+ absent or a leaf file is missing).
88
+ """
89
+ rows = []
90
+ for row in DISPATCH:
91
+ path = os.path.join(AEROSKILLS, "skills", row["leaf"],
92
+ "scripts", row["logic"])
93
+ if not os.path.exists(path):
94
+ continue
95
+ spec = importlib.util.spec_from_file_location("hs_dispatch", path)
96
+ mod = importlib.util.module_from_spec(spec)
97
+ try:
98
+ spec.loader.exec_module(mod)
99
+ except Exception:
100
+ continue
101
+ fn = getattr(mod, row["fn"], None)
102
+ if fn is None:
103
+ continue
104
+ try:
105
+ skill_out = fn(**row["args"])
106
+ skill_value = skill_out[row["key"]] if isinstance(skill_out, dict) \
107
+ else skill_out
108
+ core_value = _core_value(row)
109
+ if not isinstance(skill_value, (int, float)) or \
110
+ not isinstance(core_value, (int, float)):
111
+ continue
112
+ delta = abs(float(core_value) - float(skill_value))
113
+ rows.append({
114
+ "leaf": row["leaf"],
115
+ "skill_md": "SKILL.md",
116
+ "logic_file": "scripts/" + row["logic"],
117
+ "function": row["fn"],
118
+ "dispatched": True,
119
+ "cross_check_point": row["point"],
120
+ "core_value": round(float(core_value), 6),
121
+ "skill_value": round(float(skill_value), 6),
122
+ "delta": round(delta, 9),
123
+ "agrees": delta <= row["tol"],
124
+ "tolerance": row["tol"],
125
+ "skills_release": SKILLS_RELEASE,
126
+ })
127
+ except Exception:
128
+ continue
129
+ return rows
130
+
131
+
132
+ def _provenance(item, model, dispatch_rows):
133
+ return {
134
+ "role": ROLE_SLUG,
135
+ "core": {
136
+ "file": "core/high_speed_aero_core.py",
137
+ "functions": ["total_static_ratios", "normal_shock",
138
+ "oblique_shock", "shock_expansion_airfoil",
139
+ "flat_plate_thicknesses", "flat_plate_transition",
140
+ "bow_shock_standoff_distance",
141
+ "stagnation_heat_flux"],
142
+ "version": "0.1.0",
143
+ },
144
+ "skills": dispatch_rows,
145
+ "cross_checked": bool(dispatch_rows and all(
146
+ r.get("agrees") for r in dispatch_rows)),
147
+ "disclaimer": "DRAFT for human review. Not an approval document.",
148
+ }
149
+
150
+
151
+ def cmd_build(args):
152
+ item = core.example_item()
153
+ if args.mach is not None:
154
+ if not (1.0 < args.mach < 4.0):
155
+ print("error: --mach must be in (1.0, 4.0) for this memo")
156
+ return 1
157
+ item.cruise_mach = args.mach
158
+ if args.altitude is not None:
159
+ item.cruise_altitude_m = args.altitude
160
+ if args.climb is not None:
161
+ item.climb_mach = args.climb
162
+ try:
163
+ model = core.build_memo(item)
164
+ except ValueError as e:
165
+ print("error: %s" % e)
166
+ print("note: the reference planform keeps a subsonic leading edge "
167
+ "(M cos(Lambda) < 1) and its supercritical section stays below "
168
+ "drag divergence M_DD at the effective Mach; the default "
169
+ "condition is M 2.0")
170
+ return 1
171
+ md = core.render_memo_markdown(model)
172
+
173
+ # program profile (customer tailoring): load + apply context header
174
+ profile = None
175
+ try:
176
+ profile = evidence.load_profile(args.profile)
177
+ except ValueError as e:
178
+ print("error: bad profile: %s" % e)
179
+ return 1
180
+ if profile:
181
+ model["profile"] = {
182
+ "customer": profile.get("customer"),
183
+ "program": profile.get("program"),
184
+ "basis": profile.get("basis"),
185
+ "authority": profile.get("authority"),
186
+ "der": profile.get("der"),
187
+ "document_prefix": profile.get("document_prefix"),
188
+ "revision": profile.get("revision"),
189
+ }
190
+ md = evidence.profile_header_block(profile) + md
191
+
192
+ gates = core.check_memo(model)
193
+
194
+ dispatch_rows = []
195
+ if not args.no_dispatch:
196
+ dispatch_rows = _dispatch_crosschecks(item, model)
197
+
198
+ if args.out:
199
+ with open(args.out, "w") as f:
200
+ f.write(md if md.endswith("\n") else md + "\n")
201
+ print("built high-speed aerodynamic analysis memo for %s -> %s"
202
+ % (model["vehicle"], args.out))
203
+ if profile:
204
+ print("profile: %s / %s" % (profile.get("customer"),
205
+ profile.get("program")))
206
+ print("M=%.2f p0/p=%.3f A/A*=%.4f recovery2=%.4f "
207
+ "M_DD=%.3f T0=%.1f K"
208
+ % (model["cruise_mach"], model["total_ratios"]["p0_over_p"],
209
+ model["area_ratio"], model["shocks"]["recovery_total"],
210
+ model["corrections"]["m_dd"],
211
+ model["total_conditions"]["t0"]))
212
+ print("gates: all_pass=%s %s" % (gates["all_pass"], gates))
213
+ if args.bundle:
214
+ prov = _provenance(item, model, dispatch_rows)
215
+ paths = evidence.write_bundle(
216
+ args.out, ROLE_SLUG,
217
+ "high-speed aerodynamic analysis memo", model, gates, prov)
218
+ print("bundle: model=%s" % paths["model"])
219
+ print(" gates=%s" % paths["gates"])
220
+ print(" provenance=%s" % paths["provenance"])
221
+ if dispatch_rows:
222
+ print("cross-check: %d leaf logic file(s) dispatched"
223
+ % len(dispatch_rows))
224
+ for r in dispatch_rows:
225
+ print(" %s core=%.6f skill=%.6f delta=%.9f agrees=%s"
226
+ % (r["leaf"].split("/")[-1], r["core_value"],
227
+ r["skill_value"], r["delta"], r["agrees"]))
228
+ else:
229
+ print("cross-check: not dispatched (no AeroSkills logic)")
230
+ else:
231
+ print(md)
232
+ return 0 if gates["all_pass"] else 1
233
+
234
+
235
+ def cmd_check(args):
236
+ if not os.path.exists(args.file):
237
+ print("file not found: %s" % args.file)
238
+ return 1
239
+ md = open(args.file).read()
240
+ gates = core.check_memo_markdown(md)
241
+ for name, ok in gates.items():
242
+ if name != "all_pass":
243
+ print("%s %s" % ("PASS" if ok else "FAIL", name))
244
+ print("RESULT: %s" % ("PASS" if gates["all_pass"] else "FAIL"))
245
+ return 0 if gates["all_pass"] else 1
246
+
247
+
248
+ def main():
249
+ p = argparse.ArgumentParser(
250
+ description="High-Speed Aerodynamics Engineer role")
251
+ sub = p.add_subparsers(dest="cmd", required=True)
252
+
253
+ b = sub.add_parser("build")
254
+ b.add_argument("--out", default="", help="output file (default: stdout)")
255
+ b.add_argument("--mach", type=float, default=None,
256
+ help="cruise Mach override (default 2.0; up to ~2.2 keeps "
257
+ "the section below drag divergence M_DD 0.908 at the "
258
+ "effective Mach)")
259
+ b.add_argument("--altitude", type=float, default=None,
260
+ help="cruise altitude override in metres (default 18288)")
261
+ b.add_argument("--climb", type=float, default=None,
262
+ help="transonic climb check Mach override (default 0.85)")
263
+ b.add_argument("--bundle", action="store_true",
264
+ help="emit evidence/{model,gates,provenance}.json")
265
+ b.add_argument("--no-dispatch", action="store_true",
266
+ help="skip dispatching bound AeroSkills logic")
267
+ b.add_argument("--profile", default="",
268
+ help="program profile JSON (customer tailoring, "
269
+ "docs/PROFILE-SCHEMA.md)")
270
+ b.set_defaults(fn=cmd_build)
271
+
272
+ c = sub.add_parser("check")
273
+ c.add_argument("--file", required=True,
274
+ help="high-speed analysis memo markdown to gate-check")
275
+ c.set_defaults(fn=cmd_check)
276
+
277
+ args = p.parse_args()
278
+ sys.exit(args.fn(args))
279
+
280
+
281
+ if __name__ == "__main__":
282
+ main()