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,157 @@
1
+ ---
2
+ type: role
3
+ name: ndt-engineer
4
+ title: "NDT Engineer"
5
+ status: draft
6
+ domain: manufacturing-quality
7
+ deliverable_type: "nondestructive test plan + method selection report"
8
+ standards_bound:
9
+ - id: as9100
10
+ tier: TIER-2
11
+ reference-only: true
12
+ - id: nas-410
13
+ tier: TIER-2
14
+ reference-only: true
15
+ skills_bound:
16
+ - manufacturing-quality/ndt/ndt-method-selection
17
+ - manufacturing-quality/ndt/eddy-current-inspection
18
+ - manufacturing-quality/ndt/liquid-penetrant-inspection
19
+ - manufacturing-quality/ndt/radiographic-inspection
20
+ - manufacturing-quality/ndt/computed-tomography
21
+ - manufacturing-quality/ndt/magnetic-particle-inspection
22
+ - manufacturing-quality/ndt/leak-testing
23
+ - manufacturing-quality/ndt/acoustic-emission-inspection
24
+ - manufacturing-quality/ndt/shearography-inspection
25
+ - manufacturing-quality/ndt/ndt-personnel-qualification
26
+ tools_allowed: [stdlib, offline-file-processing]
27
+ forbidden:
28
+ - "issue acceptance or disposition approval"
29
+ - "certify NDT personnel"
30
+ - "approve a qualified procedure"
31
+ - "claim certification authority"
32
+ sign_off_required: true
33
+ license: Apache-2.0
34
+ compatibility: "agentskills.io ROLE.md; any SKILL.md host"
35
+ metadata:
36
+ version: 0.1.0
37
+ author: ashfordeOU
38
+ skills_release: "v1.3.0+"
39
+ ---
40
+
41
+ # NDT Engineer
42
+
43
+ ## Role identity
44
+
45
+ Selects non-destructive testing (NDT) methods for an aerospace part
46
+ from its candidate defect types and material class, computes the
47
+ inspection parameters of the selected methods (eddy current frequency
48
+ and penetration, penetrant dwell and indication sizing, radiography
49
+ technique checks, CT resolution and porosity, magnetic particle
50
+ magnetization, leak test rate and disposition), frames acceptance and
51
+ disposition rules, and checks NDT personnel qualification. Use when a
52
+ forged/cast/machined aerospace part needs a defensible NDT inspection
53
+ plan and method selection before the qualified procedure is written.
54
+ Do NOT use for the inspection itself, for writing the qualified
55
+ procedure, or where no acceptance document exists.
56
+
57
+ ## Deliverable contract
58
+
59
+ The role produces the **Nondestructive Test Plan and Method Selection
60
+ Report** (per templates/ndt-plan-template.md):
61
+ 1. **Method selection** — candidate defect population (surface /
62
+ near-surface / internal x ferromagnetic / non-ferromagnetic /
63
+ non-conductive) mapped to applicable methods, the selected
64
+ highest-sensitivity method per defect, alternates, rationale.
65
+ 2. **Inspection parameters** — computed settings per selected method:
66
+ ET frequency / standard depth of penetration / density ratio and
67
+ phase lag; PT capillary pressure, Washburn dwell sizing, bleed-out
68
+ indication width, developer coverage; RT geometric unsharpness,
69
+ inverse-square exposure, IQI sensitivity, film density verdict;
70
+ CT magnification / voxel resolution / projections / kV / porosity;
71
+ MT magnetization current, tangential field, particle sensitivity,
72
+ bath concentration, residual field; leak decay rate and gauge
73
+ adequacy.
74
+ 3. **Personnel qualification** — per-method certification level
75
+ check, recertification / near-vision currency, supervision pairing.
76
+ 4. **Acceptance and disposition framing** — disposition rules per
77
+ method as INPUTS for the responsible engineering authority.
78
+
79
+ ## Workflow (stages → bound skills)
80
+
81
+ | Stage | Loads skill | Produces |
82
+ |---|---|---|
83
+ | 1. Method selection per defect class | ndt/ndt-method-selection | applicable method set, selected method, alternates, rationale |
84
+ | 2. Eddy current parameter set | ndt/eddy-current-inspection | frequency, standard depth of penetration, density ratio, phase lag |
85
+ | 3. Liquid penetrant dwell sizing | ndt/liquid-penetrant-inspection | capillary pressure, Washburn depth/dwell, bleed-out sizing |
86
+ | 4. Radiography technique verdict | ndt/radiographic-inspection | geometric unsharpness, exposure, IQI sensitivity, film density, setup verdict |
87
+ | 5. Computed tomography resolution plan | ndt/computed-tomography | magnification, voxel/resolution check, projections, kV, porosity |
88
+ | 6. Magnetic particle magnetization plan | ndt/magnetic-particle-inspection | magnetizing current, field verdict, particle/bath checks, residual field |
89
+ | 7. Leak test plan and disposition | ndt/leak-testing | method recommendation, decay rate, disposition, gauge adequacy |
90
+ | 8. Acoustic emission monitoring plan | ndt/acoustic-emission-inspection | hit/event grouping, source location, Kaiser/Felicity verdicts |
91
+ | 9. Shearography strain plan | ndt/shearography-inspection | shear, min detectable strain, load step, anomaly disposition |
92
+ | 10. Personnel qualification review | ndt/ndt-personnel-qualification | recert/vision currency, supervision, upgrade eligibility |
93
+
94
+ Stages 1-5 and 7 are exercised end-to-end by the example item
95
+ (cast aluminum housing); the ferromagnetic (MT), composite (shearography)
96
+ and monitoring (AE) stages bind leaves whose math the core engine
97
+ implements and tests, and which fire for the parts that need them.
98
+
99
+ ## Evidence gates
100
+
101
+ - Gate 1 — method selection complete: every candidate defect has a
102
+ selected method, alternates and rationale.
103
+ - Gate 2 — parameters computed: every method card in the plan carries
104
+ computed numeric parameters.
105
+ - Gate 3 — personnel qualified: operator record current, supervision
106
+ pairing valid, per-method certification levels met.
107
+ - Gate 4 — acceptance framed: disposition rules present for every
108
+ method in the plan.
109
+ - Gate 5 — sign-off honest: the deliverable is marked draft-for-review
110
+ and is never an approval.
111
+ - FINAL — every number in the report traces to a bound ndt leaf logic
112
+ file (mirrored in the core; cross-checked when AeroSkills is present).
113
+
114
+ ## Boundary / forbidden
115
+
116
+ - NEVER issue acceptance or disposition approval — the responsible
117
+ engineering authority and the acceptance document dispose.
118
+ - NEVER certify NDT personnel or approve a qualified procedure —
119
+ the employer's written practice and NAS 410 govern.
120
+ - NEVER claim certification authority; the report is a DRAFT for
121
+ human NDT engineering review.
122
+ - NEVER reproduce AS9100D or NAS 410 text — paraphrase and reference
123
+ only, per STANDARDS.md.
124
+
125
+ ## Verification
126
+
127
+ The role runs STANDALONE: `core/ndt_core.py` is an executable engine
128
+ that computes method selection, per-method parameters, personnel
129
+ qualification and acceptance framing, BUILDS the report, and
130
+ gate-checks deliverables. No AeroSkills checkout required.
131
+
132
+ Run the role:
133
+ ```bash
134
+ python3 cli.py build --out plan.md # example item (cast Al)
135
+ python3 cli.py build --out plan.md --bundle # + evidence bundle
136
+ python3 cli.py build --leak-limit-sccs 0.1 # re-run leak disposition
137
+ python3 cli.py check --file plan.md # gate-check a report
138
+ ```
139
+
140
+ Tests:
141
+ - tests/test_ndt_core.py: domain rules (method selection, ET/PT/RT/CT/
142
+ MT/leak/AE/shearography math anchored to the bound leaf logic
143
+ values) + report builder + gates + standalone (no skills repo).
144
+ - tests/test_role_ndt_engineer.py: bound-skill resolution (skips if
145
+ the skills repo is absent) + workflow + template present + boundaries.
146
+ - tests/test_ndt_cli_bundle.py: build --bundle evidence protocol
147
+ (model/gates/provenance JSON), skill dispatch cross-checks when
148
+ AeroSkills is present, standalone honesty, check --file.
149
+
150
+ Bound skills in Aero Agent Skills deepen the individual stages when the
151
+ library is present (dispatch cross-check); the core engine does not
152
+ depend on them.
153
+
154
+ ## Compliance
155
+
156
+ - as9100 + nas-410 TIER-2 reference-only per standards-map; no verbatim.
157
+ - SOURCES.md records acquisition/extraction/verification status.
@@ -0,0 +1,18 @@
1
+ # SOURCES.md - NDT Engineer
2
+
3
+ Standards and documents this role references. Summary-not-copy: see
4
+ [STANDARDS.md](../../STANDARDS.md) for the full rule and purchase links.
5
+
6
+ | Source | Role use | Gated |
7
+ |---|---|---|
8
+ | AS9100D | frames NDT as a special process requiring qualified procedures, personnel and records | true |
9
+ | NAS 410 | NDT personnel qualification (Levels I-III): recertification interval, near-vision examination, supervision rule | true |
10
+ | ASTM E1444/E1742-family method practice | common-practice magnetization current rules, density bands and IQI practice as summarized (paraphrase-only) in the bound ndt leaf logic | true |
11
+
12
+ The report template in `templates/` and every formula in the core are
13
+ original structures and math mirrored from the bound
14
+ `manufacturing-quality/ndt/*` leaf logic (same org, Apache-2.0) and
15
+ common NDT methodology - no proprietary standard text is reproduced.
16
+ The personnel levels/intervals and method thresholds are documented
17
+ paraphrase-safe defaults from the bound leaves; the qualified procedure
18
+ and the employer's written practice govern real settings.
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env python3
2
+ """cli.py - run the NDT Engineer role.
3
+
4
+ Usage:
5
+ python3 cli.py build [--out <file.md>] [--bundle] [--profile <p.json>]
6
+ [--leak-limit-sccs <limit>] [--no-dispatch]
7
+ # build the Nondestructive Test Plan and Method Selection Report for
8
+ # the example item (critical cast aluminum gearbox housing);
9
+ # --leak-limit-sccs re-runs the leak disposition against another
10
+ # allowable; --bundle emits evidence/{model,gates,provenance}.json
11
+ python3 cli.py check --file <file.md>
12
+ # gate-check an existing report document
13
+
14
+ The role ENGINE (core/ndt_core.py) does the work standalone: method
15
+ selection per defect/material class, inspection parameters per method,
16
+ personnel qualification, acceptance framing, and the evidence gates.
17
+ When AeroSkills is present (AEROSKILLS_DEV or ~/AeroSkills), the CLI
18
+ dispatches the bound ndt leaf logic and cross-checks it against the
19
+ core's computation - two independent implementations agreeing is
20
+ recorded in provenance.json.
21
+ """
22
+ import argparse
23
+ import importlib.util
24
+ import os
25
+ import sys
26
+
27
+ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
28
+ "core"))
29
+ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
30
+ "..", "..", "scripts"))
31
+ import ndt_core as core # noqa: E402
32
+ import evidence # noqa: E402
33
+
34
+ ROLE_SLUG = "ndt-engineer"
35
+ SKILLS_ROOT = os.environ.get("AEROSKILLS_DEV",
36
+ os.path.expanduser("~/AeroSkills"))
37
+ _NDT_PREFIX = os.path.join(SKILLS_ROOT, "skills", "manufacturing-quality",
38
+ "ndt")
39
+ SKILLS_RELEASE = "v1.3.0+"
40
+
41
+
42
+ def _load_leaf_fn(leaf, fn_name):
43
+ """Import a bound ndt leaf's scripts module exposing fn_name."""
44
+ scripts = os.path.join(_NDT_PREFIX, leaf, "scripts")
45
+ if not os.path.isdir(scripts):
46
+ return None
47
+ for f in sorted(os.listdir(scripts)):
48
+ if not f.endswith(".py") or f.startswith("test_"):
49
+ continue
50
+ spec = importlib.util.spec_from_file_location(
51
+ "ndt_" + leaf.replace("-", "_"), os.path.join(scripts, f))
52
+ mod = importlib.util.module_from_spec(spec)
53
+ try:
54
+ spec.loader.exec_module(mod)
55
+ except Exception:
56
+ continue
57
+ if hasattr(mod, fn_name):
58
+ return mod
59
+ return None
60
+
61
+
62
+ def _row(leaf, logic_file_hint, fn_name, skill_mod, core_value, skill_value,
63
+ point, tolerance=1e-6):
64
+ delta = abs(float(core_value) - float(skill_value))
65
+ return {
66
+ "leaf": "manufacturing-quality/ndt/" + leaf,
67
+ "skill_md": "SKILL.md",
68
+ "logic_file": logic_file_hint,
69
+ "function": fn_name,
70
+ "dispatched": True,
71
+ "cross_check_point": point,
72
+ "core_value": round(float(core_value), 6),
73
+ "skill_value": round(float(skill_value), 6),
74
+ "delta": round(delta, 9),
75
+ "agrees": delta <= tolerance,
76
+ "tolerance": tolerance,
77
+ "skills_release": SKILLS_RELEASE,
78
+ }
79
+
80
+
81
+ def _dispatch_crosschecks(model):
82
+ """Cross-check core numbers against the bound AeroSkills ndt logic.
83
+
84
+ One numeric pair per method family present in the model:
85
+ ET frequency selection, PT capillary pressure, RT exposure + IQI,
86
+ CT projection/tube energy/porosity/void diameter, leak decay rate
87
+ and gauge time. Rows are absent when the skills checkout (or a
88
+ leaf) is absent; the role then simply ran standalone.
89
+ """
90
+ rows = []
91
+ cards = model.get("parameter_cards", {})
92
+
93
+ def try_row(leaf, fn_name, skill_kwargs, core_value, point,
94
+ logic_file=None):
95
+ mod = _load_leaf_fn(leaf, fn_name)
96
+ if mod is None or core_value is None:
97
+ return
98
+ try:
99
+ skill_value = getattr(mod, fn_name)(**skill_kwargs)
100
+ lf = logic_file or os.path.basename(mod.__file__ or leaf)
101
+ rows.append(_row(leaf, lf, fn_name, mod, core_value,
102
+ skill_value, point))
103
+ except Exception:
104
+ return
105
+
106
+ if "eddy-current-surface" in cards:
107
+ c = cards["eddy-current-surface"]
108
+ try_row("eddy-current-inspection", "select_frequency_for_flaw",
109
+ {"flaw_depth": c["inputs"]["flaw_depth_m"],
110
+ "conductivity": core.conductivity_from_iacs(c["percent_iacs"]),
111
+ "penetration_factor": c["inputs"]["penetration_factor"]},
112
+ c["frequency_hz"],
113
+ "ET surface zone: frequency for a %.3g m flaw at factor %.1f"
114
+ % (c["flaw_depth_m"], c["penetration_factor"]))
115
+ if "eddy-current-subsurface" in cards:
116
+ c = cards["eddy-current-subsurface"]
117
+ try_row("eddy-current-inspection", "select_frequency_for_flaw",
118
+ {"flaw_depth": c["inputs"]["flaw_depth_m"],
119
+ "conductivity": core.conductivity_from_iacs(c["percent_iacs"]),
120
+ "penetration_factor": c["inputs"]["penetration_factor"]},
121
+ c["frequency_hz"],
122
+ "ET subsurface zone: frequency for a %.3g m flaw at factor "
123
+ "%.1f" % (c["flaw_depth_m"], c["penetration_factor"]))
124
+ if "liquid-penetrant" in cards:
125
+ c = cards["liquid-penetrant"]
126
+ try_row("liquid-penetrant-inspection", "capillary_pressure",
127
+ {"surface_tension": c["surface_tension_nm"],
128
+ "contact_angle_deg": c["contact_angle_deg"],
129
+ "radius": c["effective_capillary_radius_m"]},
130
+ c["capillary_pressure_pa"],
131
+ "PT: capillary pressure across the meniscus of the %.3g m "
132
+ "radius crack" % c["effective_capillary_radius_m"])
133
+ try_row("liquid-penetrant-inspection", "washburn_penetration_depth",
134
+ {"surface_tension": c["surface_tension_nm"],
135
+ "contact_angle_deg": c["contact_angle_deg"],
136
+ "viscosity": c["viscosity_pas"],
137
+ "radius": c["effective_capillary_radius_m"],
138
+ "time": c["reference_dwell_s"]},
139
+ c["washburn_depth_at_reference_s_m"],
140
+ "PT: Washburn penetration at the %g s reference dwell"
141
+ % c["reference_dwell_s"])
142
+ if "radiography" in cards:
143
+ c = cards["radiography"]
144
+ try_row("radiographic-inspection", "exposure_time",
145
+ {"base_time": c["inputs"]["base_exposure_min"],
146
+ "distance": c["sod_mm"] + c["odd_mm"],
147
+ "reference_distance": c["inputs"]["reference_distance_mm"]},
148
+ c["exposure_min"],
149
+ "RT: inverse-square exposure at the working distance")
150
+ try_row("radiographic-inspection", "iqi_sensitivity_percent",
151
+ {"visible_thickness_mm": c["iq_visible_mm"],
152
+ "part_thickness_mm": c["section_thickness_mm"]},
153
+ c["iqi_sensitivity_percent"],
154
+ "RT: IQI sensitivity on the %.0f mm section"
155
+ % c["section_thickness_mm"])
156
+ if "computed-tomography" in cards:
157
+ c = cards["computed-tomography"]
158
+ try_row("computed-tomography", "projection_count",
159
+ {"columns_span": c["inputs"]["columns_span"]},
160
+ c["projection_count"],
161
+ "CT: cone-beam projection count for %d columns"
162
+ % c["inputs"]["columns_span"])
163
+ try_row("computed-tomography", "tube_energy_kv",
164
+ {"material": c["inputs"]["material"],
165
+ "thickness_mm": c["inputs"]["thickness_mm"]},
166
+ c["tube_energy_kv"],
167
+ "CT: tube energy for %s at %.0f mm"
168
+ % (c["inputs"]["material"], c["inputs"]["thickness_mm"]))
169
+ try_row("computed-tomography", "porosity_fraction",
170
+ {"void_voxels": c["inputs"]["void_voxels"],
171
+ "total_voxels": c["inputs"]["total_voxels"]},
172
+ c["porosity_percent"],
173
+ "CT: porosity volume fraction in the ROI")
174
+ try_row("computed-tomography", "void_diameter",
175
+ {"void_voxels": c["inputs"]["void_voxels"],
176
+ "voxel_size_m": c["voxel_size_m"]},
177
+ c["void_diameter_m"],
178
+ "CT: equivalent spherical void diameter")
179
+ if "leak-test" in cards:
180
+ c = cards["leak-test"]
181
+ try_row("leak-testing", "pressure_decay_rate",
182
+ {"volume_L": c["volume_L"], "dP_bar": c["dP_bar"],
183
+ "time_s": c["time_s"]},
184
+ c["leak_rate_sccs"],
185
+ "LT: pressure-decay leak rate over the %.0f s test"
186
+ % c["time_s"])
187
+ try_row("leak-testing", "gauge_resolution_time",
188
+ {"volume_L": c["volume_L"], "gauge_res_bar": c["gauge_res_bar"],
189
+ "target_sccs": c["max_allowable_sccs"]},
190
+ c["gauge_resolution_time_s"],
191
+ "LT: time for the gauge to resolve the allowable leak")
192
+ return rows
193
+
194
+
195
+ def _provenance(model, dispatch_rows):
196
+ return {
197
+ "role": ROLE_SLUG,
198
+ "core": {
199
+ "file": "core/ndt_core.py",
200
+ "functions": ["select_method", "et_plan", "pt_plan", "rt_plan",
201
+ "ct_plan", "mt_plan", "leak_plan",
202
+ "qualification_review", "acceptance_framing",
203
+ "build_report", "render_report_markdown",
204
+ "check_report"],
205
+ "version": "0.1.0",
206
+ },
207
+ "skills": dispatch_rows,
208
+ "cross_checked": bool(dispatch_rows),
209
+ "disclaimer": "DRAFT for human review. Not an approval document.",
210
+ }
211
+
212
+
213
+ def cmd_build(args):
214
+ item = core.example_item()
215
+ if args.leak_limit_sccs is not None:
216
+ if item.leak:
217
+ item.leak["max_allowable_sccs"] = float(args.leak_limit_sccs)
218
+ model = core.build_report(item)
219
+ md = core.render_report_markdown(model)
220
+
221
+ profile = None
222
+ try:
223
+ profile = evidence.load_profile(args.profile)
224
+ except ValueError as e:
225
+ print("error: bad profile: %s" % e)
226
+ return 1
227
+ if profile:
228
+ model["profile"] = {
229
+ "customer": profile.get("customer"),
230
+ "program": profile.get("program"),
231
+ "basis": profile.get("basis"),
232
+ "authority": profile.get("authority"),
233
+ "der": profile.get("der"),
234
+ "document_prefix": profile.get("document_prefix"),
235
+ "revision": profile.get("revision"),
236
+ }
237
+ md = evidence.profile_header_block(profile) + md
238
+
239
+ gates = core.check_report(model)
240
+ dispatch_rows = [] if args.no_dispatch else _dispatch_crosschecks(model)
241
+ if args.out:
242
+ with open(args.out, "w") as f:
243
+ f.write(md if md.endswith("\n") else md + "\n")
244
+ tops = [r["top_method"] for r in model["defect_population"]]
245
+ print("built NDT plan + method selection report -> %s" % args.out)
246
+ print("item: %s | selected methods: %s"
247
+ % (model["item"], ", ".join(tops)))
248
+ print("leak disposition: %s (%.2f dB)"
249
+ % (model["parameter_cards"]["leak-test"]["verdict"],
250
+ model["parameter_cards"]["leak-test"]["margin_db"])
251
+ if "leak-test" in model["parameter_cards"] else "")
252
+ print("gates: all_pass=%s %s" % (gates["all_pass"], gates))
253
+ if args.bundle:
254
+ prov = _provenance(model, dispatch_rows)
255
+ paths = evidence.write_bundle(
256
+ args.out, ROLE_SLUG,
257
+ "Nondestructive Test Plan and Method Selection Report",
258
+ model, gates, prov)
259
+ print("bundle: model=%s" % paths["model"])
260
+ print(" gates=%s" % paths["gates"])
261
+ print(" provenance=%s" % paths["provenance"])
262
+ if dispatch_rows:
263
+ print("cross-checks: %d pair(s) vs bound ndt leaf logic"
264
+ % len(dispatch_rows))
265
+ for row in dispatch_rows:
266
+ print(" %s/%s delta=%.2e agrees=%s"
267
+ % (row["leaf"], row["function"], row["delta"],
268
+ row["agrees"]))
269
+ else:
270
+ print(md)
271
+ return 0 if gates["all_pass"] else 1
272
+
273
+
274
+ def cmd_check(args):
275
+ if not os.path.exists(args.file):
276
+ print("file not found: %s" % args.file)
277
+ return 1
278
+ md = open(args.file).read()
279
+ gates = core.check_report_markdown(md)
280
+ for name, ok in gates.items():
281
+ if name != "all_pass":
282
+ print("%s %s" % ("PASS" if ok else "FAIL", name))
283
+ print("RESULT: %s" % ("PASS" if gates["all_pass"] else "FAIL"))
284
+ return 0 if gates["all_pass"] else 1
285
+
286
+
287
+ def main():
288
+ p = argparse.ArgumentParser(description="NDT Engineer role")
289
+ sub = p.add_subparsers(dest="cmd", required=True)
290
+
291
+ b = sub.add_parser("build")
292
+ b.add_argument("--out", default="",
293
+ help="output file (default: stdout)")
294
+ b.add_argument("--leak-limit-sccs", type=float, default=None,
295
+ help="re-run the leak disposition against this allowable "
296
+ "(scc/s)")
297
+ b.add_argument("--no-dispatch", action="store_true",
298
+ help="skip skill-logic cross-checks (standalone)")
299
+ b.add_argument("--bundle", action="store_true",
300
+ help="emit evidence/{model,gates,provenance}.json")
301
+ b.add_argument("--profile", default="",
302
+ help="program profile JSON (customer tailoring, "
303
+ "docs/PROFILE-SCHEMA.md)")
304
+ b.set_defaults(fn=cmd_build)
305
+
306
+ c = sub.add_parser("check")
307
+ c.add_argument("--file", required=True,
308
+ help="report markdown file to gate-check")
309
+ c.set_defaults(fn=cmd_check)
310
+
311
+ args = p.parse_args()
312
+ sys.exit(args.fn(args))
313
+
314
+
315
+ if __name__ == "__main__":
316
+ main()