aero-agent-roles 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +18 -0
- package/README.md +49 -0
- package/bin/aero-agent-roles.js +127 -0
- package/lib/catalog.js +97 -0
- package/lib/install.js +48 -0
- package/lib/mcp.js +199 -0
- package/manifest.json +516 -0
- package/package.json +44 -0
- package/roles/aerodynamics-engineer/ROLE.md +153 -0
- package/roles/aerodynamics-engineer/SOURCES.md +12 -0
- package/roles/aerodynamics-engineer/cli.py +143 -0
- package/roles/aerodynamics-engineer/core/aerodynamics_core.py +1218 -0
- package/roles/aerodynamics-engineer/templates/aero-design-report-template.md +72 -0
- package/roles/aerodynamics-engineer/tests/test_aerodynamics_core.py +227 -0
- package/roles/aerodynamics-engineer/tests/test_role_aerodynamics.py +94 -0
- package/roles/aircraft-design-engineer/ROLE.md +138 -0
- package/roles/aircraft-design-engineer/SOURCES.md +12 -0
- package/roles/aircraft-design-engineer/cli.py +152 -0
- package/roles/aircraft-design-engineer/core/aircraft_design_core.py +1278 -0
- package/roles/aircraft-design-engineer/templates/concept-design-template.md +99 -0
- package/roles/aircraft-design-engineer/tests/test_aircraft_design_core.py +269 -0
- package/roles/aircraft-design-engineer/tests/test_role_aircraft_design_engineer.py +106 -0
- package/roles/airworthiness-compliance-engineer/ROLE.md +122 -0
- package/roles/airworthiness-compliance-engineer/SOURCES.md +16 -0
- package/roles/airworthiness-compliance-engineer/cli.py +146 -0
- package/roles/airworthiness-compliance-engineer/core/airworthiness_core.py +894 -0
- package/roles/airworthiness-compliance-engineer/templates/compliance-matrix-template.md +46 -0
- package/roles/airworthiness-compliance-engineer/tests/test_airworthiness_core.py +224 -0
- package/roles/airworthiness-compliance-engineer/tests/test_role_airworthiness.py +85 -0
- package/roles/as9100-quality-auditor/ROLE.md +128 -0
- package/roles/as9100-quality-auditor/SOURCES.md +16 -0
- package/roles/as9100-quality-auditor/cli.py +136 -0
- package/roles/as9100-quality-auditor/core/as9100_core.py +857 -0
- package/roles/as9100-quality-auditor/templates/findings-report-template.md +119 -0
- package/roles/as9100-quality-auditor/tests/test_as9100_cli_profile.py +105 -0
- package/roles/as9100-quality-auditor/tests/test_as9100_core.py +204 -0
- package/roles/as9100-quality-auditor/tests/test_role_as9100.py +90 -0
- package/roles/data-bus-avionics-engineer/ROLE.md +146 -0
- package/roles/data-bus-avionics-engineer/SOURCES.md +23 -0
- package/roles/data-bus-avionics-engineer/cli.py +311 -0
- package/roles/data-bus-avionics-engineer/core/data_bus_avionics_core.py +1121 -0
- package/roles/data-bus-avionics-engineer/templates/bus-assessment-template.md +134 -0
- package/roles/data-bus-avionics-engineer/tests/test_bundle_protocol.py +137 -0
- package/roles/data-bus-avionics-engineer/tests/test_data_bus_avionics_core.py +261 -0
- package/roles/data-bus-avionics-engineer/tests/test_role_data_bus_avionics_engineer.py +121 -0
- package/roles/do160-environmental-engineer/ROLE.md +136 -0
- package/roles/do160-environmental-engineer/SOURCES.md +30 -0
- package/roles/do160-environmental-engineer/cli.py +147 -0
- package/roles/do160-environmental-engineer/core/do160_environmental_core.py +1246 -0
- package/roles/do160-environmental-engineer/templates/environmental-qualification-plan-template.md +114 -0
- package/roles/do160-environmental-engineer/tests/test_do160_environmental_core.py +312 -0
- package/roles/do160-environmental-engineer/tests/test_role_do160_environmental_engineer.py +281 -0
- package/roles/do178c-cert-engineer/ROLE.md +120 -0
- package/roles/do178c-cert-engineer/SOURCES.md +16 -0
- package/roles/do178c-cert-engineer/cli.py +129 -0
- package/roles/do178c-cert-engineer/core/do178c_core.py +411 -0
- package/roles/do178c-cert-engineer/templates/psac-template.md +74 -0
- package/roles/do178c-cert-engineer/tests/test_do178c_cert_engineer_core.py +91 -0
- package/roles/do178c-cert-engineer/tests/test_role_do178c.py +109 -0
- package/roles/do254-hardware-engineer/ROLE.md +127 -0
- package/roles/do254-hardware-engineer/SOURCES.md +19 -0
- package/roles/do254-hardware-engineer/cli.py +308 -0
- package/roles/do254-hardware-engineer/core/do254_hardware_core.py +761 -0
- package/roles/do254-hardware-engineer/templates/phac-template.md +87 -0
- package/roles/do254-hardware-engineer/tests/test_do254_bundle_profile.py +152 -0
- package/roles/do254-hardware-engineer/tests/test_do254_hardware_core.py +200 -0
- package/roles/do254-hardware-engineer/tests/test_role_do254_hardware.py +108 -0
- package/roles/engineering-analysis-engineer/ROLE.md +154 -0
- package/roles/engineering-analysis-engineer/SOURCES.md +15 -0
- package/roles/engineering-analysis-engineer/cli.py +151 -0
- package/roles/engineering-analysis-engineer/core/engineering_analysis_engineer_core.py +866 -0
- package/roles/engineering-analysis-engineer/templates/analysis-memo-template.md +73 -0
- package/roles/engineering-analysis-engineer/tests/test_engineering_analysis_engineer_core.py +367 -0
- package/roles/engineering-analysis-engineer/tests/test_role_engineering_analysis_engineer.py +113 -0
- package/roles/flight-management-engineer/ROLE.md +160 -0
- package/roles/flight-management-engineer/SOURCES.md +30 -0
- package/roles/flight-management-engineer/cli.py +258 -0
- package/roles/flight-management-engineer/core/flight_management_core.py +1473 -0
- package/roles/flight-management-engineer/templates/route-assessment-template.md +94 -0
- package/roles/flight-management-engineer/tests/test_flight_management_core.py +437 -0
- package/roles/flight-management-engineer/tests/test_role_flight_management_engineer.py +123 -0
- package/roles/flight-mechanics-engineer/ROLE.md +162 -0
- package/roles/flight-mechanics-engineer/SOURCES.md +51 -0
- package/roles/flight-mechanics-engineer/cli.py +149 -0
- package/roles/flight-mechanics-engineer/core/flight_mechanics_core.py +1507 -0
- package/roles/flight-mechanics-engineer/templates/perf-sc-report-template.md +82 -0
- package/roles/flight-mechanics-engineer/tests/test_flight_mechanics_core.py +277 -0
- package/roles/flight-mechanics-engineer/tests/test_role_flight_mechanics_engineer.py +141 -0
- package/roles/flight-test-engineer/ROLE.md +137 -0
- package/roles/flight-test-engineer/SOURCES.md +24 -0
- package/roles/flight-test-engineer/cli.py +135 -0
- package/roles/flight-test-engineer/core/flight_test_core.py +931 -0
- package/roles/flight-test-engineer/templates/flight-test-template.md +143 -0
- package/roles/flight-test-engineer/tests/test_flight_test_engineer_core.py +227 -0
- package/roles/flight-test-engineer/tests/test_role_flight_test_engineer.py +150 -0
- package/roles/flight-test-performance-engineer/ROLE.md +165 -0
- package/roles/flight-test-performance-engineer/SOURCES.md +20 -0
- package/roles/flight-test-performance-engineer/cli.py +319 -0
- package/roles/flight-test-performance-engineer/core/flight_test_performance_core.py +1398 -0
- package/roles/flight-test-performance-engineer/templates/performance-report-template.md +105 -0
- package/roles/flight-test-performance-engineer/tests/test_flight_test_performance_engineer_core.py +308 -0
- package/roles/flight-test-performance-engineer/tests/test_role_flight_test_performance_engineer.py +165 -0
- package/roles/gnc-engineer/ROLE.md +141 -0
- package/roles/gnc-engineer/SOURCES.md +35 -0
- package/roles/gnc-engineer/cli.py +141 -0
- package/roles/gnc-engineer/core/gnc_core.py +1074 -0
- package/roles/gnc-engineer/templates/gnc-report-template.md +105 -0
- package/roles/gnc-engineer/tests/test_gnc_engineer_core.py +238 -0
- package/roles/gnc-engineer/tests/test_role_gnc_engineer.py +142 -0
- package/roles/guidance-engineer/ROLE.md +156 -0
- package/roles/guidance-engineer/SOURCES.md +43 -0
- package/roles/guidance-engineer/cli.py +280 -0
- package/roles/guidance-engineer/core/guidance_core.py +820 -0
- package/roles/guidance-engineer/templates/guidance-report-template.md +70 -0
- package/roles/guidance-engineer/tests/test_guidance_engineer_core.py +269 -0
- package/roles/guidance-engineer/tests/test_role_guidance_engineer.py +146 -0
- package/roles/high-speed-aerodynamics-engineer/ROLE.md +161 -0
- package/roles/high-speed-aerodynamics-engineer/SOURCES.md +18 -0
- package/roles/high-speed-aerodynamics-engineer/cli.py +282 -0
- package/roles/high-speed-aerodynamics-engineer/core/high_speed_aero_core.py +1566 -0
- package/roles/high-speed-aerodynamics-engineer/templates/high-speed-memo-template.md +75 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_bundle_protocol.py +103 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_high_speed_aero_core.py +280 -0
- package/roles/high-speed-aerodynamics-engineer/tests/test_role_high_speed_aerodynamics.py +125 -0
- package/roles/ndt-engineer/ROLE.md +157 -0
- package/roles/ndt-engineer/SOURCES.md +18 -0
- package/roles/ndt-engineer/cli.py +316 -0
- package/roles/ndt-engineer/core/ndt_core.py +1590 -0
- package/roles/ndt-engineer/templates/ndt-plan-template.md +109 -0
- package/roles/ndt-engineer/tests/test_ndt_cli_bundle.py +183 -0
- package/roles/ndt-engineer/tests/test_ndt_core.py +437 -0
- package/roles/ndt-engineer/tests/test_role_ndt_engineer.py +148 -0
- package/roles/numerical-analysis-engineer/ROLE.md +158 -0
- package/roles/numerical-analysis-engineer/SOURCES.md +19 -0
- package/roles/numerical-analysis-engineer/cli.py +414 -0
- package/roles/numerical-analysis-engineer/core/numerical_analysis_core.py +1220 -0
- package/roles/numerical-analysis-engineer/templates/verification-memo-template.md +120 -0
- package/roles/numerical-analysis-engineer/tests/test_bundle_protocol.py +157 -0
- package/roles/numerical-analysis-engineer/tests/test_numerical_analysis_core.py +337 -0
- package/roles/numerical-analysis-engineer/tests/test_role_numerical_analysis_engineer.py +117 -0
- package/roles/propulsion-engineer/ROLE.md +163 -0
- package/roles/propulsion-engineer/SOURCES.md +15 -0
- package/roles/propulsion-engineer/cli.py +168 -0
- package/roles/propulsion-engineer/core/propulsion_core.py +1291 -0
- package/roles/propulsion-engineer/templates/propulsion-report-template.md +92 -0
- package/roles/propulsion-engineer/tests/test_propulsion_engineer_core.py +294 -0
- package/roles/propulsion-engineer/tests/test_role_propulsion_engineer.py +106 -0
- package/roles/space-systems-engineer/ROLE.md +161 -0
- package/roles/space-systems-engineer/SOURCES.md +49 -0
- package/roles/space-systems-engineer/cli.py +173 -0
- package/roles/space-systems-engineer/core/space_systems_core.py +980 -0
- package/roles/space-systems-engineer/templates/space-systems-template.md +88 -0
- package/roles/space-systems-engineer/tests/test_role_space_systems_engineer.py +117 -0
- package/roles/space-systems-engineer/tests/test_space_systems_engineer_core.py +247 -0
- package/roles/state-estimation-engineer/ROLE.md +154 -0
- package/roles/state-estimation-engineer/SOURCES.md +27 -0
- package/roles/state-estimation-engineer/cli.py +326 -0
- package/roles/state-estimation-engineer/core/state_estimation_core.py +958 -0
- package/roles/state-estimation-engineer/templates/nav-state-estimator-design-template.md +76 -0
- package/roles/state-estimation-engineer/tests/test_bundle_protocol.py +110 -0
- package/roles/state-estimation-engineer/tests/test_role_state_estimation_engineer.py +127 -0
- package/roles/state-estimation-engineer/tests/test_state_estimation_engineer_core.py +231 -0
- package/roles/structures-loads-engineer/ROLE.md +171 -0
- package/roles/structures-loads-engineer/SOURCES.md +44 -0
- package/roles/structures-loads-engineer/cli.py +219 -0
- package/roles/structures-loads-engineer/core/structures_loads_core.py +1112 -0
- package/roles/structures-loads-engineer/templates/loads-strength-report-template.md +96 -0
- package/roles/structures-loads-engineer/tests/test_bundle_protocol.py +101 -0
- package/roles/structures-loads-engineer/tests/test_role_structures_loads_engineer.py +147 -0
- package/roles/structures-loads-engineer/tests/test_structures_loads_core.py +261 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# SOURCES.md - Guidance Engineer
|
|
2
|
+
|
|
3
|
+
| Source | Role use | Gated |
|
|
4
|
+
|---|---|---|
|
|
5
|
+
| ARP4754A | development-assurance context framing the guidance cluster leaves (reference only) | true |
|
|
6
|
+
| FAR-25 / CS-25 | airworthiness context where guided-vehicle development touches civil certification (reference only) | false |
|
|
7
|
+
| Zarchan, Tactical and Strategic Missile Guidance (AIAA) | classical PN/APN/ZEM guidance-law context (book, summarized via leaves) | true |
|
|
8
|
+
| Siouris, Missile Guidance and Control Systems | pursuit/CLOS/guidance-loop context (book, summarized via leaves) | true |
|
|
9
|
+
|
|
10
|
+
Summary-not-copy per STANDARDS.md. The guidance laws themselves are
|
|
11
|
+
common knowledge encoded by the bound Aero Agent Skills leaves; no
|
|
12
|
+
standard or book text is reproduced.
|
|
13
|
+
|
|
14
|
+
## Encoded domain rules (computed by core/guidance_core.py)
|
|
15
|
+
|
|
16
|
+
The engine's numbers come from the laws carried by the bound
|
|
17
|
+
gnc-autonomy/guidance leaves (each leaf is a paraphrase of common
|
|
18
|
+
guidance knowledge); anchors are asserted in the leaf gate-3 contract
|
|
19
|
+
tests and reproduced here:
|
|
20
|
+
|
|
21
|
+
| Rule | Formula | Bound leaf | Anchor |
|
|
22
|
+
|---|---|---|---|
|
|
23
|
+
| Closing velocity | vc = -(rx*vx + ry*vy) / r | guidance/proportional-navigation | 199.007 m/s |
|
|
24
|
+
| LOS rate | lam_dot = (rx*vy - ry*vx) / r^2 | guidance/proportional-navigation | 0.019802 rad/s |
|
|
25
|
+
| PN command | a_c = N * vc * lam_dot | guidance/proportional-navigation | 15.762965 m/s^2 (N=4) |
|
|
26
|
+
| APN command | a_apn = N' * (Vc*lam_dot + a_T/2), N' = 4 default | guidance/augmented-proportional-navigation | 55.763 m/s^2 |
|
|
27
|
+
| Acceleration in g | a / g0, g0 = 9.80665 | guidance/augmented-proportional-navigation | 1.607 g |
|
|
28
|
+
| Pursuit heading error | eta = wrap(lam - psi) | guidance/pursuit-guidance | 5.711 deg |
|
|
29
|
+
| Pursuit lead angle | asin((Vt/Vi) sin(beta)) | guidance/pursuit-guidance | -1.901 deg |
|
|
30
|
+
| Capture condition | Vi > Vt | guidance/pursuit-guidance | satisfied |
|
|
31
|
+
| Tail-chase intercept | t_i = r / (Vi - Vt) | guidance/pursuit-guidance | 5.025 s |
|
|
32
|
+
| Desired course (waypoint) | psi_d = atan2(wy - py, wx - px) | guidance/midcourse-guidance | 26.565 deg |
|
|
33
|
+
| Turn-rate-limited steering | psi_c = psi + clamp(e, +/-omega*dt) | guidance/midcourse-guidance | 5 deg per step |
|
|
34
|
+
| Velocity to be gained | vgo = max(0, V_target - V cos(e)) | guidance/midcourse-guidance | 65.08 m/s |
|
|
35
|
+
| Zero-effort miss | ZEM = \|rho + v_rel t_go\|, t_go = max(0, -(rho.v_rel)/\|v_rel\|^2) | guidance/midcourse-guidance | 150 m at t_go 20 s |
|
|
36
|
+
| Miss requirement check | ZEM <= required miss distance | guidance/midcourse-guidance | 10 m req (example) |
|
|
37
|
+
| Acceleration limit | utilization = \|a_c\| / (limit_g * g0) | guidance/augmented-proportional-navigation | 5.4% PN / 19.0% APN |
|
|
38
|
+
|
|
39
|
+
All example engagement numbers (speeds, ranges, limits, requirements)
|
|
40
|
+
are stated project facts for the reference item — verify against the
|
|
41
|
+
vehicle and target data before use. Navigation constant band 3-5 with
|
|
42
|
+
4 as the common baseline is the leaf-encoded practice note; the
|
|
43
|
+
selected N remains a project decision recorded in the report.
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""cli.py - run the Guidance Engineer role.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python3 cli.py build [--out <file.md>] [--n-nav <N>] \\
|
|
6
|
+
[--miss-requirement <m>] [--accel-limit <g>] [--bundle]
|
|
7
|
+
# build the Guidance Law Design and Assessment Report for the
|
|
8
|
+
# example item (interceptor terminal homing loop)
|
|
9
|
+
# --bundle also emit evidence/{model,gates,provenance}.json (PROTOCOL.md)
|
|
10
|
+
python3 cli.py check --file <file.md> # gate-check a report
|
|
11
|
+
|
|
12
|
+
The role ENGINE (core/guidance_core.py) does the work standalone; bound
|
|
13
|
+
Aero Agent Skills leaves in gnc-autonomy/guidance carry the same laws as
|
|
14
|
+
*_logic.py files, and when the library is present their implementations
|
|
15
|
+
are dispatched on the same inputs and recorded in provenance.json
|
|
16
|
+
(delta ~ 0 = two independent implementations agree).
|
|
17
|
+
"""
|
|
18
|
+
import argparse
|
|
19
|
+
import importlib.util
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
|
|
24
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
25
|
+
"core"))
|
|
26
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
27
|
+
"..", "..", "scripts"))
|
|
28
|
+
import guidance_core as core # noqa: E402
|
|
29
|
+
import evidence # noqa: E402
|
|
30
|
+
|
|
31
|
+
ROLE_SLUG = "guidance-engineer"
|
|
32
|
+
|
|
33
|
+
# Functions the core exports with leaf-identical signatures, dispatched
|
|
34
|
+
# against the bound leaf *_logic.py when the skills tree is present.
|
|
35
|
+
DISPATCH_ROWS = [
|
|
36
|
+
{
|
|
37
|
+
"leaf": "gnc-autonomy/guidance/proportional-navigation",
|
|
38
|
+
"skill_fn": "commanded_acceleration",
|
|
39
|
+
"core_fn": "commanded_acceleration",
|
|
40
|
+
"kwargs": {"rx": 1000.0, "ry": 100.0, "vx": -200.0, "vy": 0.0,
|
|
41
|
+
"n_nav": 4.0},
|
|
42
|
+
"tolerance": 1e-6,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"leaf": "gnc-autonomy/guidance/augmented-proportional-navigation",
|
|
46
|
+
"skill_fn": "apn_command",
|
|
47
|
+
"core_fn": "apn_command",
|
|
48
|
+
"kwargs": {"navigation_ratio": 4.0,
|
|
49
|
+
"closing_velocity": 199.007438041998,
|
|
50
|
+
"los_rate": 0.019801980198,
|
|
51
|
+
"target_lateral_accel": 20.0},
|
|
52
|
+
"tolerance": 1e-6,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"leaf": "gnc-autonomy/guidance/pursuit-guidance",
|
|
56
|
+
"skill_fn": "heading_error",
|
|
57
|
+
"core_fn": "heading_error",
|
|
58
|
+
"kwargs": {"psi": 0.0, "rx": 1000.0, "ry": 100.0},
|
|
59
|
+
"tolerance": 1e-6,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"leaf": "gnc-autonomy/guidance/midcourse-guidance",
|
|
63
|
+
"skill_fn": "desired_heading",
|
|
64
|
+
"core_fn": "desired_heading",
|
|
65
|
+
"kwargs": {"position": (0.0, 0.0), "waypoint": (1000.0, 500.0)},
|
|
66
|
+
"tolerance": 1e-6,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"leaf": "gnc-autonomy/guidance/midcourse-guidance",
|
|
70
|
+
"skill_fn": "zero_effort_miss",
|
|
71
|
+
"core_fn": "zero_effort_miss",
|
|
72
|
+
"kwargs": {"interceptor_pos": (0.0, 0.0),
|
|
73
|
+
"interceptor_vel": (300.0, 0.0),
|
|
74
|
+
"target_pos": (6000.0, 150.0),
|
|
75
|
+
"target_vel": (0.0, 0.0)},
|
|
76
|
+
"value_key": "zem",
|
|
77
|
+
"tolerance": 1e-6,
|
|
78
|
+
},
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _load_skill_logic(leaf, skills_root):
|
|
83
|
+
"""Import the leaf's *_logic.py module (stdlib) or return None.
|
|
84
|
+
|
|
85
|
+
Returns (module, logic_file_basename) or (None, None)."""
|
|
86
|
+
logic_dir = os.path.join(skills_root, "skills", leaf, "scripts")
|
|
87
|
+
if not os.path.isdir(logic_dir):
|
|
88
|
+
return None, None
|
|
89
|
+
logic_files = sorted(f for f in os.listdir(logic_dir)
|
|
90
|
+
if f.endswith("_logic.py"))
|
|
91
|
+
for lf in logic_files:
|
|
92
|
+
spec = importlib.util.spec_from_file_location(
|
|
93
|
+
"role_dispatch_" + leaf.replace("/", "_"),
|
|
94
|
+
os.path.join(logic_dir, lf))
|
|
95
|
+
if spec is None or spec.loader is None:
|
|
96
|
+
continue
|
|
97
|
+
mod = importlib.util.module_from_spec(spec)
|
|
98
|
+
try:
|
|
99
|
+
spec.loader.exec_module(mod)
|
|
100
|
+
except Exception:
|
|
101
|
+
continue
|
|
102
|
+
return mod, lf
|
|
103
|
+
return None, None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _dispatch_rows(skills_root=""):
|
|
107
|
+
"""Dispatch each row against the bound leaf and the role core.
|
|
108
|
+
|
|
109
|
+
Returns provenance rows: dispatched True with core/skill value,
|
|
110
|
+
delta and agrees when the leaf logic is reachable, else a
|
|
111
|
+
dispatched False row (role core ran standalone - still valid).
|
|
112
|
+
"""
|
|
113
|
+
root = skills_root or os.environ.get(
|
|
114
|
+
"AEROSKILLS_DEV", os.path.expanduser("~/AeroSkills"))
|
|
115
|
+
rows = []
|
|
116
|
+
for row in DISPATCH_ROWS:
|
|
117
|
+
mod, logic_file = _load_skill_logic(row["leaf"], root)
|
|
118
|
+
if mod is None or not hasattr(mod, row["skill_fn"]):
|
|
119
|
+
rows.append({
|
|
120
|
+
"leaf": row["leaf"],
|
|
121
|
+
"logic_file": "n/a (standalone)",
|
|
122
|
+
"function": row["skill_fn"],
|
|
123
|
+
"dispatched": False,
|
|
124
|
+
"reason": "skill logic unavailable (standalone build)",
|
|
125
|
+
})
|
|
126
|
+
continue
|
|
127
|
+
try:
|
|
128
|
+
skill_val = getattr(mod, row["skill_fn"])(**row["kwargs"])
|
|
129
|
+
core_val = getattr(core, row["core_fn"])(**row["kwargs"])
|
|
130
|
+
if row.get("value_key"):
|
|
131
|
+
skill_val = skill_val[row["value_key"]]
|
|
132
|
+
core_val = core_val[row["value_key"]]
|
|
133
|
+
if not isinstance(skill_val, (int, float)) or \
|
|
134
|
+
not isinstance(core_val, (int, float)):
|
|
135
|
+
raise TypeError("non-numeric cross-check value")
|
|
136
|
+
delta = abs(float(core_val) - float(skill_val))
|
|
137
|
+
rows.append({
|
|
138
|
+
"leaf": row["leaf"],
|
|
139
|
+
"logic_file": "scripts/" + (logic_file or "unknown.py"),
|
|
140
|
+
"function": row["skill_fn"],
|
|
141
|
+
"dispatched": True,
|
|
142
|
+
"core_value": round(float(core_val), 6),
|
|
143
|
+
"skill_value": round(float(skill_val), 6),
|
|
144
|
+
"delta": round(delta, 9),
|
|
145
|
+
"agrees": delta <= row["tolerance"],
|
|
146
|
+
"tolerance": row["tolerance"],
|
|
147
|
+
})
|
|
148
|
+
except Exception as exc: # noqa: BLE001 - provenance must not crash
|
|
149
|
+
rows.append({
|
|
150
|
+
"leaf": row["leaf"],
|
|
151
|
+
"function": row["skill_fn"],
|
|
152
|
+
"dispatched": False,
|
|
153
|
+
"reason": "dispatch error: %s" % exc,
|
|
154
|
+
})
|
|
155
|
+
return rows
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _provenance():
|
|
159
|
+
"""Provenance for the bundle: role core + leaf dispatch rows."""
|
|
160
|
+
rows = _dispatch_rows()
|
|
161
|
+
return {
|
|
162
|
+
"role": ROLE_SLUG,
|
|
163
|
+
"core": {
|
|
164
|
+
"file": "core/guidance_core.py",
|
|
165
|
+
"functions": ["commanded_acceleration", "apn_command",
|
|
166
|
+
"heading_error", "lead_angle", "desired_heading",
|
|
167
|
+
"commanded_heading", "zero_effort_miss",
|
|
168
|
+
"accel_utilization", "miss_within_requirement",
|
|
169
|
+
"build_report", "check_report",
|
|
170
|
+
"render_report_markdown"],
|
|
171
|
+
"version": "0.1.0",
|
|
172
|
+
},
|
|
173
|
+
"skills": rows,
|
|
174
|
+
"cross_checked": any(r.get("dispatched") for r in rows),
|
|
175
|
+
"disclaimer": "DRAFT for human review. Not an approval document.",
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _model_jsonable(model):
|
|
180
|
+
"""Deep-copy the model into plain JSON types (tuples -> lists)."""
|
|
181
|
+
return json.loads(json.dumps(model, default=list))
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def cmd_build(args):
|
|
185
|
+
proj = core.example_project()
|
|
186
|
+
if args.n_nav:
|
|
187
|
+
proj.n_nav = float(args.n_nav)
|
|
188
|
+
if args.miss_requirement:
|
|
189
|
+
proj.miss_requirement_m = float(args.miss_requirement)
|
|
190
|
+
if args.accel_limit:
|
|
191
|
+
proj.accel_limit_g = float(args.accel_limit)
|
|
192
|
+
model = core.build_report(proj)
|
|
193
|
+
md = core.render_report_markdown(model)
|
|
194
|
+
|
|
195
|
+
# program profile (customer tailoring): load + apply context header
|
|
196
|
+
profile = None
|
|
197
|
+
try:
|
|
198
|
+
profile = evidence.load_profile(args.profile)
|
|
199
|
+
except ValueError as exc:
|
|
200
|
+
print("error: bad profile: %s" % exc)
|
|
201
|
+
return 1
|
|
202
|
+
if profile:
|
|
203
|
+
model["profile"] = {
|
|
204
|
+
"customer": profile.get("customer"),
|
|
205
|
+
"program": profile.get("program"),
|
|
206
|
+
"basis": profile.get("basis"),
|
|
207
|
+
"authority": profile.get("authority"),
|
|
208
|
+
"der": profile.get("der"),
|
|
209
|
+
"document_prefix": profile.get("document_prefix"),
|
|
210
|
+
"revision": profile.get("revision"),
|
|
211
|
+
}
|
|
212
|
+
md = evidence.profile_header_block(profile) + md
|
|
213
|
+
|
|
214
|
+
gates = core.check_report(model)
|
|
215
|
+
if args.out:
|
|
216
|
+
with open(args.out, "w") as f:
|
|
217
|
+
f.write(md)
|
|
218
|
+
print("built Guidance Law Design and Assessment Report (%s) -> %s"
|
|
219
|
+
% (model["item"], args.out))
|
|
220
|
+
if profile:
|
|
221
|
+
print("profile: %s / %s" % (profile.get("customer"),
|
|
222
|
+
profile.get("program")))
|
|
223
|
+
print("gates: all_pass=%s %s" % (gates["all_pass"], gates))
|
|
224
|
+
if args.bundle:
|
|
225
|
+
paths = evidence.write_bundle(
|
|
226
|
+
args.out, ROLE_SLUG,
|
|
227
|
+
"Guidance Law Design and Assessment Report",
|
|
228
|
+
_model_jsonable(model), gates, _provenance())
|
|
229
|
+
print("bundle: model=%s" % paths["model"])
|
|
230
|
+
print(" gates=%s" % paths["gates"])
|
|
231
|
+
print(" provenance=%s" % paths["provenance"])
|
|
232
|
+
else:
|
|
233
|
+
print(md)
|
|
234
|
+
return 0 if gates["all_pass"] else 1
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def cmd_check(args):
|
|
238
|
+
if not os.path.exists(args.file):
|
|
239
|
+
print("file not found: %s" % args.file)
|
|
240
|
+
return 1
|
|
241
|
+
md = open(args.file).read()
|
|
242
|
+
gates = core.check_report_markdown(md)
|
|
243
|
+
for name, ok in gates.items():
|
|
244
|
+
if name != "all_pass":
|
|
245
|
+
print("%s %s" % ("PASS" if ok else "FAIL", name))
|
|
246
|
+
print("RESULT: %s" % ("PASS" if gates["all_pass"] else "FAIL"))
|
|
247
|
+
return 0 if gates["all_pass"] else 1
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def main():
|
|
251
|
+
p = argparse.ArgumentParser(description="Guidance Engineer role")
|
|
252
|
+
sub = p.add_subparsers(dest="cmd", required=True)
|
|
253
|
+
|
|
254
|
+
b = sub.add_parser("build")
|
|
255
|
+
b.add_argument("--out", default="", help="output file (default: stdout)")
|
|
256
|
+
b.add_argument("--n-nav", default="",
|
|
257
|
+
help="navigation constant N (default 4)")
|
|
258
|
+
b.add_argument("--miss-requirement", default="",
|
|
259
|
+
help="miss distance requirement in m (default 10)")
|
|
260
|
+
b.add_argument("--accel-limit", default="",
|
|
261
|
+
help="vehicle lateral acceleration limit in g (default 30)")
|
|
262
|
+
b.add_argument("--bundle", action="store_true",
|
|
263
|
+
help="emit evidence/{model,gates,provenance}.json")
|
|
264
|
+
b.add_argument("--profile", default="",
|
|
265
|
+
help="program profile JSON (customer tailoring, "
|
|
266
|
+
"docs/PROFILE-SCHEMA.md)")
|
|
267
|
+
b.set_defaults(fn=cmd_build)
|
|
268
|
+
|
|
269
|
+
c = sub.add_parser("check")
|
|
270
|
+
c.add_argument("--file", required=True,
|
|
271
|
+
help="Guidance Law Design and Assessment Report markdown "
|
|
272
|
+
"to gate-check")
|
|
273
|
+
c.set_defaults(fn=cmd_check)
|
|
274
|
+
|
|
275
|
+
args = p.parse_args()
|
|
276
|
+
sys.exit(args.fn(args))
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
if __name__ == "__main__":
|
|
280
|
+
main()
|