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,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()
|