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,46 @@
|
|
|
1
|
+
# Airworthiness Compliance Matrix
|
|
2
|
+
|
|
3
|
+
**Item:** Autopilot flight guidance system retrofit (STC)
|
|
4
|
+
**Aircraft type:** transport (jurisdiction FAA)
|
|
5
|
+
**Certification basis:** FAR-25
|
|
6
|
+
**Certification path:** STC
|
|
7
|
+
**Applicable regulations (proposed):** 25.1301, 25.1309, 25.1329, 25.671, 25.683
|
|
8
|
+
**Special conditions:** none identified
|
|
9
|
+
**Equivalent level of safety (ELOS) findings:** none proposed
|
|
10
|
+
**Status:** draft-for-review
|
|
11
|
+
**Coverage:** 100% of applicable regulations have a proposed means of compliance and compliance document
|
|
12
|
+
|
|
13
|
+
## Certification basis
|
|
14
|
+
|
|
15
|
+
- Aircraft/system: Autopilot flight guidance system retrofit (STC)
|
|
16
|
+
- Certification basis reference (TC/STC/amendment level): FAR-25, STC
|
|
17
|
+
- Applicable regulations (list with amendment): 25.1301, 25.1309, 25.1329, 25.671, 25.683 (amendment level per the basis)
|
|
18
|
+
- Special conditions: none identified
|
|
19
|
+
- Equivalent level of safety findings: none proposed
|
|
20
|
+
|
|
21
|
+
## Compliance matrix
|
|
22
|
+
|
|
23
|
+
| Reg | Applicable? | Means of compliance | Compliance document | Status (proposed/open) | Owner |
|
|
24
|
+
|---|---|---|---|---|---|
|
|
25
|
+
| 25.1301 (Subpart F - Equipment) | YES | ground test + engineering/analysis | Equipment installation inspection records and qualification test report (installation conformity) | proposed (severity n/a / DAL n/a) | equipment engineer |
|
|
26
|
+
| 25.1309 (Subpart F - Equipment) | YES | engineering/analysis + safety assessment (safety assessment required) | System safety assessment (SSA) per 25.1309 with failure condition classification | proposed (severity hazardous / DAL B) | systems safety engineer |
|
|
27
|
+
| 25.1329 (Subpart F - Equipment) | YES | engineering/analysis + safety assessment + ground test + flight test (safety assessment required) | Autopilot ground/flight test report and SSA input for the flight guidance system | proposed (severity hazardous / DAL B) | systems safety engineer |
|
|
28
|
+
| 25.671 (Subpart D - Design and Construction) | YES | engineering/analysis + safety assessment (safety assessment required) | Flight control integration analysis and test report (control system failure assessment) | proposed (severity hazardous / DAL B) | flight controls engineer |
|
|
29
|
+
| 25.683 (Subpart D - Design and Construction) | YES | engineering/analysis + ground test | Control system operation test report | proposed (severity n/a / DAL n/a) | flight controls engineer |
|
|
30
|
+
| 25.301 (Subpart C - Structure) | NO | n/a | not required (screened out: not applicable to this change: 25.301 covers the structure area but the change scope is limited to equipment, flight-controls, systems; screened out unless the change also touches the structure area - confirm with the certification authority) | proposed (n/a) | - |
|
|
31
|
+
| 25.571 (Subpart C - Structure) | NO | n/a | not required (screened out: not applicable to this change: 25.571 covers the structure area but the change scope is limited to equipment, flight-controls, systems; screened out unless the change also touches the structure area - confirm with the certification authority) | proposed (n/a) | - |
|
|
32
|
+
| 25.607 (Subpart D - Design and Construction) | NO | n/a | not required (screened out: not applicable to this change: 25.607 covers the design-and-construction area but the change scope is limited to equipment, flight-controls, systems; screened out unless the change also touches the design-and-construction area - confirm with the certification authority) | proposed (n/a) | - |
|
|
33
|
+
|
|
34
|
+
## Notes
|
|
35
|
+
|
|
36
|
+
- Status vocabulary: proposed | open | in-work | evidence-available.
|
|
37
|
+
- Every row above is a PROPOSED determination by the compliance engineer (this role). The certification authority makes the binding applicability and compliance findings.
|
|
38
|
+
- NEVER mark a row "compliant/found" without evidence - that is the authority's finding, not ours.
|
|
39
|
+
- Severity and DAL context are proposed inputs to the 25.1309 safety assessment, subject to the system safety process.
|
|
40
|
+
|
|
41
|
+
## Open items (gap report)
|
|
42
|
+
|
|
43
|
+
- No open gaps in this draft; confirm every row with the certification authority before submission.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
*Generated by Aero Agent Roles airworthiness-compliance-engineer core (2026-09-05). DRAFT - for human compliance engineer review. Not a compliance finding or an approval.*
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Test airworthiness_core: the executable engine of the
|
|
3
|
+
airworthiness-compliance-engineer role.
|
|
4
|
+
|
|
5
|
+
Proves the role can DO its job standalone (no AeroSkills needed):
|
|
6
|
+
applicability determination, means-of-compliance selection, compliance
|
|
7
|
+
matrix generation, and evidence-gate checks.
|
|
8
|
+
|
|
9
|
+
Author: ashfordeOU
|
|
10
|
+
"""
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
import unittest
|
|
14
|
+
|
|
15
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
16
|
+
"..", "core"))
|
|
17
|
+
from airworthiness_core import ( # noqa: E402
|
|
18
|
+
MOC_IDS, MOC_NAMES, REGS, SEVERITY_TO_DAL,
|
|
19
|
+
build_matrix, check_matrix, check_matrix_markdown,
|
|
20
|
+
compliance_row, determine_applicability, example_item,
|
|
21
|
+
example_matrix_markdown, moc_is_valid, moc_suitability,
|
|
22
|
+
render_matrix_markdown, safety_assessment_required, select_moc,
|
|
23
|
+
severity_to_dal,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TestCoreDomainRules(unittest.TestCase):
|
|
28
|
+
|
|
29
|
+
def test_severity_to_dal_mapping(self):
|
|
30
|
+
self.assertEqual(SEVERITY_TO_DAL["catastrophic"], "A")
|
|
31
|
+
self.assertEqual(SEVERITY_TO_DAL["hazardous"], "B")
|
|
32
|
+
self.assertEqual(SEVERITY_TO_DAL["major"], "C")
|
|
33
|
+
self.assertEqual(SEVERITY_TO_DAL["minor"], "D")
|
|
34
|
+
self.assertEqual(severity_to_dal("hazardous"), "B")
|
|
35
|
+
|
|
36
|
+
def test_safety_assessment_rule(self):
|
|
37
|
+
# 25.1309 practice: catastrophic/hazardous/major need the SSA.
|
|
38
|
+
self.assertTrue(safety_assessment_required("catastrophic"))
|
|
39
|
+
self.assertTrue(safety_assessment_required("hazardous"))
|
|
40
|
+
self.assertTrue(safety_assessment_required("major"))
|
|
41
|
+
self.assertFalse(safety_assessment_required("minor"))
|
|
42
|
+
self.assertFalse(safety_assessment_required("no-safety-effect"))
|
|
43
|
+
with self.assertRaises(ValueError):
|
|
44
|
+
safety_assessment_required("very-bad")
|
|
45
|
+
|
|
46
|
+
def test_moc_suitability_rules(self):
|
|
47
|
+
# Structure: analysis primary, static/ground test evidence.
|
|
48
|
+
self.assertEqual(moc_suitability("structure", "n/a", "n/a"), [1, 2])
|
|
49
|
+
# Novel structure adds flight test.
|
|
50
|
+
self.assertEqual(
|
|
51
|
+
moc_suitability("structure", "n/a", "n/a", novel=True), [1, 2, 3])
|
|
52
|
+
# Hazardous systems: analysis + MOC 6 safety assessment.
|
|
53
|
+
self.assertEqual(
|
|
54
|
+
moc_suitability("systems", "hazardous", "B"), [1, 6])
|
|
55
|
+
# Equipment: qualification ground test with analysis support.
|
|
56
|
+
self.assertEqual(moc_suitability("equipment"), [2, 1])
|
|
57
|
+
with self.assertRaises(ValueError):
|
|
58
|
+
moc_suitability("quantum", "n/a", "n/a")
|
|
59
|
+
|
|
60
|
+
def test_real_regs_are_real(self):
|
|
61
|
+
# Regs in the register must be real FAR-25 paragraphs (public
|
|
62
|
+
# domain; register verified against the public eCFR titles).
|
|
63
|
+
for number, rec in REGS.items():
|
|
64
|
+
self.assertRegex(number, r"^25\.\d{3,4}$")
|
|
65
|
+
self.assertTrue(rec["title"])
|
|
66
|
+
self.assertIn(rec["subpart"], "ABCDEFGH")
|
|
67
|
+
for num in ("25.1309", "25.1329", "25.301", "25.571",
|
|
68
|
+
"25.607", "25.683", "25.671", "25.1301"):
|
|
69
|
+
self.assertIn(num, REGS)
|
|
70
|
+
|
|
71
|
+
def test_applicability_transport(self):
|
|
72
|
+
verdict = determine_applicability("25.1309", "transport")
|
|
73
|
+
self.assertTrue(verdict["applicable"])
|
|
74
|
+
verdict2 = determine_applicability("25.1329", "transport-category")
|
|
75
|
+
self.assertTrue(verdict2["applicable"])
|
|
76
|
+
|
|
77
|
+
def test_applicability_not_transport(self):
|
|
78
|
+
# Part 25 paragraphs do not apply to a normal-category airplane
|
|
79
|
+
# (that certifies under Part 23 / CS-23).
|
|
80
|
+
verdict = determine_applicability("25.1309", "normal")
|
|
81
|
+
self.assertFalse(verdict["applicable"])
|
|
82
|
+
self.assertIn("Part 23", verdict["reason"])
|
|
83
|
+
with self.assertRaises(ValueError):
|
|
84
|
+
determine_applicability("25.1309", "airship")
|
|
85
|
+
with self.assertRaises(ValueError):
|
|
86
|
+
determine_applicability("99.9999", "transport")
|
|
87
|
+
with self.assertRaises(ValueError):
|
|
88
|
+
determine_applicability("25.9999", "transport")
|
|
89
|
+
|
|
90
|
+
def test_applicability_change_scope(self):
|
|
91
|
+
# Structure paragraphs screen out when the STC touches only
|
|
92
|
+
# systems/flight-controls/equipment.
|
|
93
|
+
scope = ["systems", "flight-controls", "equipment"]
|
|
94
|
+
self.assertTrue(
|
|
95
|
+
determine_applicability("25.1309", "transport", scope)["applicable"])
|
|
96
|
+
self.assertFalse(
|
|
97
|
+
determine_applicability("25.571", "transport", scope)["applicable"])
|
|
98
|
+
self.assertFalse(
|
|
99
|
+
determine_applicability("25.301", "transport", scope)["applicable"])
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class TestMocSelection(unittest.TestCase):
|
|
103
|
+
|
|
104
|
+
def test_select_moc_far25_vocabulary(self):
|
|
105
|
+
moc = select_moc("25.1309", "FAR-25")
|
|
106
|
+
self.assertEqual(moc["reg"], "25.1309")
|
|
107
|
+
self.assertEqual(moc["item_kind"], "systems")
|
|
108
|
+
self.assertEqual(moc["dal"], "B") # hazardous context
|
|
109
|
+
self.assertTrue(moc["safety_assessment"])
|
|
110
|
+
# Real vocabulary words only.
|
|
111
|
+
for m in moc["methods"]:
|
|
112
|
+
self.assertTrue(moc_is_valid(m))
|
|
113
|
+
self.assertIn("analysis", moc["moc_display"])
|
|
114
|
+
self.assertIn("safety assessment", moc["moc_display"])
|
|
115
|
+
|
|
116
|
+
def test_select_moc_cs25_scheme(self):
|
|
117
|
+
moc = select_moc("25.1309", "CS-25")
|
|
118
|
+
self.assertIn("MOC", moc["moc_display"])
|
|
119
|
+
self.assertIn("MOC 6 safety assessment", moc["moc_display"])
|
|
120
|
+
# ids must all be real MOC class ids
|
|
121
|
+
for i in moc["moc_ids"]:
|
|
122
|
+
self.assertIn(i, MOC_IDS)
|
|
123
|
+
|
|
124
|
+
def test_select_moc_extras_and_novelty(self):
|
|
125
|
+
# Autopilot flight guidance is novel on the type: ground test is
|
|
126
|
+
# added; the reg carries flight test from demonstration practice.
|
|
127
|
+
moc = select_moc("25.1329", "FAR-25", novel=True)
|
|
128
|
+
self.assertIn("flight test", moc["methods"])
|
|
129
|
+
self.assertIn(2, moc["moc_ids"]) # test MOC present for novel item
|
|
130
|
+
# 25.683 operation tests carries a ground test MOC.
|
|
131
|
+
moc683 = select_moc("25.683", "FAR-25")
|
|
132
|
+
self.assertIn("ground test", moc683["methods"])
|
|
133
|
+
|
|
134
|
+
def test_moc_names_real(self):
|
|
135
|
+
self.assertEqual(MOC_NAMES[1], "engineering/analysis")
|
|
136
|
+
self.assertEqual(MOC_NAMES[5], "certification by similarity")
|
|
137
|
+
self.assertEqual(MOC_NAMES[6], "safety assessment")
|
|
138
|
+
self.assertFalse(moc_is_valid("quantum compliance"))
|
|
139
|
+
with self.assertRaises(ValueError):
|
|
140
|
+
select_moc("25.9999", "FAR-25")
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class TestMatrixBuilder(unittest.TestCase):
|
|
144
|
+
|
|
145
|
+
def test_build_matrix_example(self):
|
|
146
|
+
model = build_matrix(example_item())
|
|
147
|
+
self.assertEqual(model["certification_basis"], "FAR-25")
|
|
148
|
+
self.assertEqual(model["status"], "draft-for-review")
|
|
149
|
+
# 8 candidate regs: 5 applicable, 3 screened out.
|
|
150
|
+
self.assertEqual(model["row_count"], 8)
|
|
151
|
+
self.assertEqual(model["applicable_count"], 5)
|
|
152
|
+
self.assertEqual(model["coverage"], 1.0)
|
|
153
|
+
self.assertEqual(model["issues"], [])
|
|
154
|
+
# The applicable autopilot regs are present.
|
|
155
|
+
regs_app = {r["reg"] for r in model["rows"] if r["applicable"]}
|
|
156
|
+
self.assertEqual(regs_app,
|
|
157
|
+
{"25.1301", "25.1309", "25.1329", "25.671", "25.683"})
|
|
158
|
+
|
|
159
|
+
def test_compliance_row_fields(self):
|
|
160
|
+
row = compliance_row("25.1309", example_item())
|
|
161
|
+
self.assertTrue(row["applicable"])
|
|
162
|
+
self.assertEqual(row["status"], "proposed")
|
|
163
|
+
self.assertEqual(row["dal"], "B")
|
|
164
|
+
self.assertTrue(row["safety_assessment"])
|
|
165
|
+
self.assertIn("System safety assessment", row["compliance_document"])
|
|
166
|
+
self.assertTrue(row["owner"])
|
|
167
|
+
# No blank fields anywhere in the row.
|
|
168
|
+
for v in row.values():
|
|
169
|
+
self.assertNotIn("___", str(v))
|
|
170
|
+
|
|
171
|
+
def test_matrix_has_no_blanks(self):
|
|
172
|
+
md = example_matrix_markdown()
|
|
173
|
+
self.assertNotIn("___", md)
|
|
174
|
+
self.assertGreater(len(md), 1500)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class TestGates(unittest.TestCase):
|
|
178
|
+
|
|
179
|
+
def test_core_gates_all_pass(self):
|
|
180
|
+
model = build_matrix(example_item())
|
|
181
|
+
gates = check_matrix(model)
|
|
182
|
+
self.assertTrue(gates["all_pass"], gates)
|
|
183
|
+
|
|
184
|
+
def test_core_gates_fail_on_missing_doc(self):
|
|
185
|
+
model = build_matrix(example_item())
|
|
186
|
+
# Break a row: applicable reg without a compliance document.
|
|
187
|
+
row = next(r for r in model["rows"] if r["applicable"])
|
|
188
|
+
row["compliance_document"] = ""
|
|
189
|
+
gates = check_matrix(model)
|
|
190
|
+
self.assertFalse(gates["all_pass"])
|
|
191
|
+
self.assertFalse(gates["all_applicable_have_doc"])
|
|
192
|
+
|
|
193
|
+
def test_core_gates_fail_on_found_status(self):
|
|
194
|
+
model = build_matrix(example_item())
|
|
195
|
+
model["rows"][0]["status"] = "compliant"
|
|
196
|
+
gates = check_matrix(model)
|
|
197
|
+
self.assertFalse(gates["all_pass"])
|
|
198
|
+
self.assertFalse(gates["statuses_honest"])
|
|
199
|
+
|
|
200
|
+
def test_markdown_gates_all_pass(self):
|
|
201
|
+
md = example_matrix_markdown()
|
|
202
|
+
gates = check_matrix_markdown(md)
|
|
203
|
+
self.assertTrue(gates["all_pass"], gates)
|
|
204
|
+
|
|
205
|
+
def test_markdown_gates_fail_on_blank(self):
|
|
206
|
+
md = example_matrix_markdown().replace("## Compliance matrix",
|
|
207
|
+
"## Compliance ___ matrix")
|
|
208
|
+
gates = check_matrix_markdown(md)
|
|
209
|
+
self.assertFalse(gates["all_pass"])
|
|
210
|
+
self.assertFalse(gates["no_blank_fields"])
|
|
211
|
+
|
|
212
|
+
def test_standalone_no_skills_repo(self):
|
|
213
|
+
# Core must work with no AeroSkills present.
|
|
214
|
+
os.environ["AEROSKILLS_DEV"] = "/nonexistent"
|
|
215
|
+
model = build_matrix(example_item())
|
|
216
|
+
self.assertTrue(model["row_count"] >= 5)
|
|
217
|
+
self.assertTrue(model["coverage"] >= 0.5)
|
|
218
|
+
md = render_matrix_markdown(model)
|
|
219
|
+
self.assertGreater(len(md), 1500)
|
|
220
|
+
self.assertTrue(check_matrix_markdown(md)["all_pass"])
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
if __name__ == "__main__":
|
|
224
|
+
unittest.main()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Role test: Airworthiness Compliance Engineer.
|
|
3
|
+
|
|
4
|
+
Offline verification:
|
|
5
|
+
1. Every bound skill resolves in aero-agent-skills.
|
|
6
|
+
2. Workflow stage order deterministic + complete.
|
|
7
|
+
3. Compliance matrix template has the required columns/rows.
|
|
8
|
+
4. Boundaries + sign-off present.
|
|
9
|
+
"""
|
|
10
|
+
import os
|
|
11
|
+
import re
|
|
12
|
+
import unittest
|
|
13
|
+
|
|
14
|
+
ROLES_REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(
|
|
15
|
+
os.path.abspath(__file__)))))
|
|
16
|
+
AEROSKILLS = os.environ.get("AEROSKILLS_DEV", os.path.expanduser("~/AeroSkills"))
|
|
17
|
+
# Bound-skill resolution requires the Aero Agent Skills checkout (cross-repo
|
|
18
|
+
# dev check); skip when absent so fresh public clones can still run make validate.
|
|
19
|
+
HAS_SKILLS = os.path.isdir(os.path.join(AEROSKILLS, "skills"))
|
|
20
|
+
ROLE_DIR = os.path.join(ROLES_REPO, "roles", "airworthiness-compliance-engineer")
|
|
21
|
+
MATRIX = os.path.join(ROLE_DIR, "templates", "compliance-matrix-template.md")
|
|
22
|
+
|
|
23
|
+
EXPECTED_BOUND = [
|
|
24
|
+
"avionics/far-cs25/airworthiness",
|
|
25
|
+
"avionics/far-cs25/special-conditions",
|
|
26
|
+
"systems-engineering-safety/certification/certification-basis",
|
|
27
|
+
"systems-engineering-safety/certification/equivalent-level-of-safety",
|
|
28
|
+
"systems-engineering-safety/certification/means-of-compliance",
|
|
29
|
+
"systems-engineering-safety/certification/mmel-development",
|
|
30
|
+
"systems-engineering-safety/continued-airworthiness/airworthiness-directive-compliance",
|
|
31
|
+
"systems-engineering-safety/continued-airworthiness/type-certificate-data-sheet",
|
|
32
|
+
"systems-engineering-safety/continued-airworthiness/in-service-safety-assessment",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
STAGES = ["Certification basis", "Special conditions", "ELOS",
|
|
36
|
+
"Means of compliance", "Airworthiness mapping", "MMEL",
|
|
37
|
+
"Continued airworthiness", "Compliance matrix"]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def role_text():
|
|
41
|
+
p = os.path.join(ROLE_DIR, "ROLE.md")
|
|
42
|
+
return open(p).read() if os.path.exists(p) else None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class TestAirworthinessRole(unittest.TestCase):
|
|
46
|
+
|
|
47
|
+
def test_bound_skills_resolve(self):
|
|
48
|
+
if not HAS_SKILLS:
|
|
49
|
+
self.skipTest("Aero Agent Skills checkout not present (cross-repo dev check)")
|
|
50
|
+
for leaf in EXPECTED_BOUND:
|
|
51
|
+
self.assertTrue(
|
|
52
|
+
os.path.exists(os.path.join(AEROSKILLS, "skills", leaf, "SKILL.md")),
|
|
53
|
+
f"bound skill missing: {leaf}")
|
|
54
|
+
|
|
55
|
+
def test_stages_ordered(self):
|
|
56
|
+
role = role_text()
|
|
57
|
+
self.assertIsNotNone(role)
|
|
58
|
+
idx = [role.find(s) for s in STAGES]
|
|
59
|
+
self.assertTrue(all(i >= 0 for i in idx))
|
|
60
|
+
self.assertEqual(idx, sorted(idx), "stage order not deterministic")
|
|
61
|
+
|
|
62
|
+
def test_matrix_columns_present(self):
|
|
63
|
+
if not os.path.exists(MATRIX):
|
|
64
|
+
self.fail("compliance-matrix-template.md missing")
|
|
65
|
+
text = open(MATRIX).read()
|
|
66
|
+
for col in ["Reg", "Applicable?", "Means of compliance",
|
|
67
|
+
"Compliance document", "Status", "Owner"]:
|
|
68
|
+
self.assertIn(col, text, f"column missing: {col}")
|
|
69
|
+
self.assertIn("proposed", text.lower())
|
|
70
|
+
|
|
71
|
+
def test_boundaries(self):
|
|
72
|
+
role = role_text()
|
|
73
|
+
self.assertIsNotNone(role)
|
|
74
|
+
for phrase in ["declare a compliance finding", "claim certification approval",
|
|
75
|
+
"sign_off_required: true"]:
|
|
76
|
+
self.assertIn(phrase, role)
|
|
77
|
+
|
|
78
|
+
def test_sources_register(self):
|
|
79
|
+
self.assertTrue(
|
|
80
|
+
os.path.exists(os.path.join(ROLE_DIR, "SOURCES.md")),
|
|
81
|
+
"SOURCES.md missing")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
unittest.main()
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: role
|
|
3
|
+
name: as9100-quality-auditor
|
|
4
|
+
title: "AS9100 Quality / Internal Auditor"
|
|
5
|
+
status: draft
|
|
6
|
+
domain: manufacturing-quality
|
|
7
|
+
deliverable_type: "audit plan + findings report + corrective-action follow-up"
|
|
8
|
+
standards_bound:
|
|
9
|
+
- id: as9100
|
|
10
|
+
tier: TIER-2
|
|
11
|
+
reference-only: true
|
|
12
|
+
skills_bound:
|
|
13
|
+
- manufacturing-quality/as9100/internal-quality-audit
|
|
14
|
+
- manufacturing-quality/as9100/document-control
|
|
15
|
+
- manufacturing-quality/as9100/management-review
|
|
16
|
+
- manufacturing-quality/as9100/nonconformance-control
|
|
17
|
+
- manufacturing-quality/as9100/corrective-action
|
|
18
|
+
- manufacturing-quality/as9100/risk-management
|
|
19
|
+
- manufacturing-quality/as9100/supplier-control
|
|
20
|
+
- manufacturing-quality/as9100/order-requirements-review
|
|
21
|
+
- manufacturing-quality/as9100/calibration-control
|
|
22
|
+
- manufacturing-quality/as9100/counterfeit-prevention
|
|
23
|
+
- manufacturing-quality/as9100/fod-control
|
|
24
|
+
- manufacturing-quality/as9100/quality
|
|
25
|
+
- manufacturing-quality/as9102/first-article-inspection
|
|
26
|
+
- manufacturing-quality/as9102/delta-fai
|
|
27
|
+
- manufacturing-quality/as9102/fai-revalidation
|
|
28
|
+
tools_allowed: [stdlib, offline-file-processing]
|
|
29
|
+
forbidden:
|
|
30
|
+
- "issue certification"
|
|
31
|
+
- "claim audit closure"
|
|
32
|
+
- "recommend supplier approval"
|
|
33
|
+
sign_off_required: true
|
|
34
|
+
license: Apache-2.0
|
|
35
|
+
compatibility: "agentskills.io ROLE.md; any SKILL.md host"
|
|
36
|
+
metadata:
|
|
37
|
+
version: 0.1.0
|
|
38
|
+
author: Aero Agent Roles
|
|
39
|
+
skills_release: "v1.3.0+"
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
# AS9100 Quality / Internal Auditor
|
|
43
|
+
|
|
44
|
+
## Role identity
|
|
45
|
+
|
|
46
|
+
Plans and executes an AS9100 quality-management-system internal audit:
|
|
47
|
+
scope, criteria, evidence collection against the standard's clauses,
|
|
48
|
+
findings classification, and corrective-action follow-up. Use when a
|
|
49
|
+
manufacturing/supplier site must demonstrate QMS conformity (internal
|
|
50
|
+
audit, supplier audit prep, management-review input). Do NOT use for
|
|
51
|
+
design/engineering work, or for external certification audits (that is a
|
|
52
|
+
certification body's role).
|
|
53
|
+
|
|
54
|
+
## Deliverable contract
|
|
55
|
+
|
|
56
|
+
1. **Audit plan** — scope, criteria (AS9100 clauses + customer/supplier
|
|
57
|
+
requirements), schedule, auditee areas.
|
|
58
|
+
2. **Findings report** — nonconformities classified (major/minor/
|
|
59
|
+
observation) with objective evidence + clause references.
|
|
60
|
+
3. **Corrective-action follow-up** — root-cause verification, action
|
|
61
|
+
tracking, closure recommendation (for human QA sign-off).
|
|
62
|
+
|
|
63
|
+
## Workflow (stages → bound skills)
|
|
64
|
+
|
|
65
|
+
| Stage | Loads skill | Produces |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| 1. Audit planning | as9100/internal-quality-audit | audit plan, criteria, scope |
|
|
68
|
+
| 2. Document control check | as9100/document-control | doc-control evidence review |
|
|
69
|
+
| 3. Management review check | as9100/management-review | MR input/output evidence |
|
|
70
|
+
| 4. Risk + ops review | as9100/risk-management + quality | operational risk evidence |
|
|
71
|
+
| 5. Nonconformance + corrective action | as9100/nonconformance-control + corrective-action | NC handling evidence, CA effectiveness |
|
|
72
|
+
| 6. Supplier + purchasing | as9100/supplier-control + order-requirements-review | supplier control evidence |
|
|
73
|
+
| 7. Calibration + counterfeit + FOD | as9100/calibration-control + counterfeit-prevention + fod-control | special-process evidence |
|
|
74
|
+
| 8. FAI (AS9102) | as9102/first-article-inspection + delta-fai + fai-revalidation | FAI evidence for product audit |
|
|
75
|
+
| 9. Findings + report | (all above) | findings report + CA follow-up |
|
|
76
|
+
|
|
77
|
+
## Evidence gates
|
|
78
|
+
|
|
79
|
+
- Stage 1 done = audit plan states scope + criteria + schedule.
|
|
80
|
+
- Stage 5 done = every NC has clause ref + objective evidence.
|
|
81
|
+
- FINAL = findings classified; closure recommendation is PROPOSED (the
|
|
82
|
+
QA manager/auditor signs closure).
|
|
83
|
+
|
|
84
|
+
## Boundary / forbidden
|
|
85
|
+
|
|
86
|
+
- NEVER issue certification (external cert bodies do that).
|
|
87
|
+
- NEVER claim audit closure without human sign-off.
|
|
88
|
+
- NEVER recommend supplier approval/disapproval — report evidence only.
|
|
89
|
+
- AS9100 is proprietary (IAQG) — TIER-2 reference-only, no verbatim.
|
|
90
|
+
|
|
91
|
+
## Verification
|
|
92
|
+
|
|
93
|
+
The role runs STANDALONE: `core/as9100_core.py` is an executable engine
|
|
94
|
+
that classifies each nonconformity on the finding ladder (major/minor/
|
|
95
|
+
observation with systemic and detection/containment escalation),
|
|
96
|
+
scores corrective-action records on the closure chain (containment ->
|
|
97
|
+
root cause -> corrective action -> effectiveness), sizes the record
|
|
98
|
+
sample, computes the next audit due date from the process risk
|
|
99
|
+
category, BUILDS the findings report, and gate-checks deliverables. No
|
|
100
|
+
AeroSkills checkout required.
|
|
101
|
+
|
|
102
|
+
Run the role:
|
|
103
|
+
```bash
|
|
104
|
+
python3 cli.py build --out findings.md # example supplier audit
|
|
105
|
+
python3 cli.py build --risk-category low --out f.md # low-risk variant
|
|
106
|
+
python3 cli.py check --file findings.md # gate-check a report
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Tests:
|
|
110
|
+
- tests/test_as9100_core.py (22 tests): classification ladder +
|
|
111
|
+
corrective-action chain + audit program numbers + findings builder +
|
|
112
|
+
gates + no-blanks render + standalone (no skills repo)
|
|
113
|
+
- tests/test_role_as9100.py: bound-skill resolution (skips if the
|
|
114
|
+
skills repo is absent) + workflow + filled template fields +
|
|
115
|
+
boundaries
|
|
116
|
+
|
|
117
|
+
`templates/findings-report-template.md` is the FILLED, generated
|
|
118
|
+
deliverable for the example audit (2 major, 2 minor, 1 observation,
|
|
119
|
+
zero blanks) — produced by `cli.py build --out`.
|
|
120
|
+
|
|
121
|
+
Bound skills in Aero Agent Skills deepen individual stages when the
|
|
122
|
+
library is present (audit scheduling, disposition, calibration,
|
|
123
|
+
corrective action); the core engine does not depend on them.
|
|
124
|
+
|
|
125
|
+
## Compliance
|
|
126
|
+
|
|
127
|
+
- as9100 TIER-2 reference-only; as9102 same family.
|
|
128
|
+
- SOURCES.md records acquisition/extraction/verification status.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# SOURCES.md - AS9100 Quality / Internal Auditor
|
|
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 | QMS requirements (the audit criteria) | true |
|
|
9
|
+
| AS9102 | First Article Inspection (product audit) | true |
|
|
10
|
+
| ISO 19011 | audit program and evidence discipline | true |
|
|
11
|
+
| ISO 9001:2015 | QMS baseline that AS9100 extends | true |
|
|
12
|
+
| IAQG public guides | supplier guidance and FAQs | false |
|
|
13
|
+
|
|
14
|
+
The findings report template in `templates/` cites clause numbers, not
|
|
15
|
+
standard text - the same practice real audit reports follow. Clause
|
|
16
|
+
references are identifiers, not reproduction.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""cli.py - run the AS9100 Quality / Internal Auditor role.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python3 cli.py build --out <file.md> # build the findings report for the example audit
|
|
6
|
+
python3 cli.py build --out <file.md> --bundle # + evidence/{model,gates,provenance}.json
|
|
7
|
+
python3 cli.py build --out <file.md> --bundle --profile <profile.json>
|
|
8
|
+
# + program profile (audited supplier context,
|
|
9
|
+
# docs/PROFILE-SCHEMA.md)
|
|
10
|
+
python3 cli.py check --file <file.md> # gate-check an existing findings report
|
|
11
|
+
|
|
12
|
+
The role ENGINE (core/as9100_core.py) does the work standalone; bound
|
|
13
|
+
skills in Aero Agent Skills deepen individual stages when present.
|
|
14
|
+
"""
|
|
15
|
+
import argparse
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
18
|
+
|
|
19
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "core"))
|
|
20
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
21
|
+
"..", "..", "scripts"))
|
|
22
|
+
import as9100_core as core
|
|
23
|
+
import evidence # noqa: E402
|
|
24
|
+
|
|
25
|
+
ROLE_SLUG = "as9100-quality-auditor"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _provenance():
|
|
29
|
+
"""Provenance for the evidence bundle: role core functions used."""
|
|
30
|
+
return {
|
|
31
|
+
"role": ROLE_SLUG,
|
|
32
|
+
"core": {
|
|
33
|
+
"file": "core/as9100_core.py",
|
|
34
|
+
"functions": ["classify_nc", "corrective_action_status",
|
|
35
|
+
"audit_sample_size", "audit_due_date",
|
|
36
|
+
"audit_findings", "check_findings"],
|
|
37
|
+
"version": "0.1.0",
|
|
38
|
+
},
|
|
39
|
+
"skills": [],
|
|
40
|
+
"cross_checked": False,
|
|
41
|
+
"disclaimer": "DRAFT for human review. Not an approval document.",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def cmd_build(args):
|
|
46
|
+
item = core.example_item()
|
|
47
|
+
if args.risk_category:
|
|
48
|
+
item.process_risk = args.risk_category
|
|
49
|
+
if args.audit_date:
|
|
50
|
+
item.audit_date = args.audit_date
|
|
51
|
+
model = core.audit_findings(item)
|
|
52
|
+
md = core.render_findings_markdown(model)
|
|
53
|
+
|
|
54
|
+
# program profile (audited supplier context): load + apply context header
|
|
55
|
+
profile = None
|
|
56
|
+
try:
|
|
57
|
+
profile = evidence.load_profile(args.profile)
|
|
58
|
+
except ValueError as e:
|
|
59
|
+
print(f"error: bad profile: {e}")
|
|
60
|
+
return 1
|
|
61
|
+
if profile:
|
|
62
|
+
model["profile"] = {
|
|
63
|
+
"customer": profile.get("customer"),
|
|
64
|
+
"program": profile.get("program"),
|
|
65
|
+
"basis": profile.get("basis"),
|
|
66
|
+
"authority": profile.get("authority"),
|
|
67
|
+
"der": profile.get("der"),
|
|
68
|
+
"document_prefix": profile.get("document_prefix"),
|
|
69
|
+
"revision": profile.get("revision"),
|
|
70
|
+
}
|
|
71
|
+
md = evidence.profile_header_block(profile) + md
|
|
72
|
+
|
|
73
|
+
gates = core.check_findings(model)
|
|
74
|
+
if args.out:
|
|
75
|
+
with open(args.out, "w") as f:
|
|
76
|
+
f.write(md)
|
|
77
|
+
print("built findings report for %s -> %s" % (model["supplier"],
|
|
78
|
+
args.out))
|
|
79
|
+
if profile:
|
|
80
|
+
print("profile: %s / %s" % (profile.get("customer"),
|
|
81
|
+
profile.get("program")))
|
|
82
|
+
print("counts: %s" % model["counts"])
|
|
83
|
+
print("gates: all_pass=%s %s" % (gates["all_pass"], gates))
|
|
84
|
+
if args.bundle:
|
|
85
|
+
paths = evidence.write_bundle(
|
|
86
|
+
args.out, ROLE_SLUG,
|
|
87
|
+
"AS9100 internal audit findings report", model, gates,
|
|
88
|
+
_provenance())
|
|
89
|
+
print("bundle: model=%s" % paths["model"])
|
|
90
|
+
print(" gates=%s" % paths["gates"])
|
|
91
|
+
print(" provenance=%s" % paths["provenance"])
|
|
92
|
+
else:
|
|
93
|
+
print(md)
|
|
94
|
+
return 0 if gates["all_pass"] else 1
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def cmd_check(args):
|
|
98
|
+
if not os.path.exists(args.file):
|
|
99
|
+
print("file not found: %s" % args.file)
|
|
100
|
+
return 1
|
|
101
|
+
md = open(args.file).read()
|
|
102
|
+
gates = core.check_findings_markdown(md)
|
|
103
|
+
for name, ok in gates.items():
|
|
104
|
+
if name != "all_pass":
|
|
105
|
+
print("%s %s" % ("PASS" if ok else "FAIL", name))
|
|
106
|
+
print("RESULT: %s" % ("PASS" if gates["all_pass"] else "FAIL"))
|
|
107
|
+
return 0 if gates["all_pass"] else 1
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def main():
|
|
111
|
+
p = argparse.ArgumentParser(description="AS9100 Quality / Internal Auditor role")
|
|
112
|
+
sub = p.add_subparsers(dest="cmd", required=True)
|
|
113
|
+
|
|
114
|
+
b = sub.add_parser("build")
|
|
115
|
+
b.add_argument("--out", default="", help="output file (default: stdout)")
|
|
116
|
+
b.add_argument("--risk-category", default="",
|
|
117
|
+
help="low/medium/high process risk")
|
|
118
|
+
b.add_argument("--audit-date", default="", help="audit date ISO YYYY-MM-DD")
|
|
119
|
+
b.add_argument("--bundle", action="store_true",
|
|
120
|
+
help="emit evidence/{model,gates,provenance}.json")
|
|
121
|
+
b.add_argument("--profile", default="",
|
|
122
|
+
help="program profile JSON (audited supplier context, "
|
|
123
|
+
"docs/PROFILE-SCHEMA.md)")
|
|
124
|
+
b.set_defaults(fn=cmd_build)
|
|
125
|
+
|
|
126
|
+
c = sub.add_parser("check")
|
|
127
|
+
c.add_argument("--file", required=True,
|
|
128
|
+
help="findings report markdown file to gate-check")
|
|
129
|
+
c.set_defaults(fn=cmd_check)
|
|
130
|
+
|
|
131
|
+
args = p.parse_args()
|
|
132
|
+
sys.exit(args.fn(args))
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
if __name__ == "__main__":
|
|
136
|
+
main()
|