feat: add knowledge-driven joint design studio

This commit is contained in:
Jerry
2026-08-14 13:53:51 +08:00
parent 7d12459a32
commit 281afdfd9e
57 changed files with 269659 additions and 1152 deletions
+53
View File
@@ -0,0 +1,53 @@
# Mechanical Transmission Knowledge Base
This directory is the detached, read-only knowledge source for future
knowledge-assisted generation. It does not participate in the current CLI,
solver, CAD, validation, or joint-module execution paths.
## Safety boundary
- `knowledge_enabled` is conceptually `false` until an explicit integration
layer is added later.
- Existing code under `src/` must not import this directory.
- Knowledge entries describe facts and bind stable capability IDs to existing
implementations; they do not execute Python code.
- A mechanism is not considered supported merely because it has a topology
entry. Its `maturity` and capability bindings define how far it can run.
- Generated experiments must eventually use `output/experiments/`, never the
existing `output/runs/` production path.
## Layers
```text
schemas/ machine-readable contracts for every entry kind
ontology/ common vocabulary: ports, relations, units, maturity
components/ reusable parts and virtual references
relations/ local physical and kinematic connections
mechanisms/ executable mechanism units/topologies
compositions/ legal ways to connect mechanism units
capabilities/ bindings to solvers, layouts, CAD, and validators
evidence/ regression baselines and validation provenance
tools/ read-only consistency checks
```
The initial catalog only records capabilities already present in the project:
- `simple_2k_h`
- `simple_2k_h_cascade`
- `ferguson_wolfrom`
- the six currently registered topology relations
No new transmission structure is claimed to be executable in this first
version.
## Validate
From `backend/`:
```bash
python3 knowledge/tools/validate_knowledge.py
```
The validator uses the Python standard library. It checks entry identity,
cross-references, mechanism graph integrity, capability source locations, and
the hashes of the three legacy topology templates.
@@ -0,0 +1,187 @@
{
"schema_version": "1.0",
"id": "capability.registry.legacy",
"version": "0.1.0",
"entries": [
{
"schema_version": "1.0",
"id": "capability.parameter_solver.simple_2kh",
"version": "1.0.0",
"kind": "parameter_solver",
"implementation": {"mode": "legacy_binding", "source_path": "src/parameter_solver.py", "symbol": "solve_parameters"},
"supports": ["mechanism.planetary.simple_2k_h"],
"limitations": ["Dispatch is currently selected by legacy topology_family."],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.parameter_solver.simple_2kh_cascade",
"version": "1.0.0",
"kind": "parameter_solver",
"implementation": {"mode": "legacy_binding", "source_path": "src/parameter_solver.py", "symbol": "solve_parameters"},
"supports": ["mechanism.planetary.simple_2k_h_cascade", "composition.coaxial_serial.two_simple_2kh"],
"limitations": ["Exactly two stages are supported."],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.parameter_solver.ferguson_wolfrom",
"version": "1.0.0",
"kind": "parameter_solver",
"implementation": {"mode": "legacy_binding", "source_path": "src/parameter_solver.py", "symbol": "solve_parameters"},
"supports": ["mechanism.planetary.ferguson_wolfrom"],
"limitations": ["Required gear tooth counts are supplied explicitly by the requirement."],
"maturity": "parameter_solved"
},
{
"schema_version": "1.0",
"id": "capability.kinematics.graph",
"version": "1.0.0",
"kind": "kinematic_solver",
"implementation": {"mode": "legacy_binding", "source_path": "src/kinematics.py", "symbol": "solve_instance"},
"supports": ["relation.mesh.external_cylindrical", "relation.mesh.internal_cylindrical", "relation.joint.fixed", "relation.joint.rigid"],
"limitations": ["Only registered legacy relation equations are executable."],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.layout.simple_2kh",
"version": "1.0.0",
"kind": "layout_solver",
"implementation": {"mode": "legacy_binding", "source_path": "src/placement/simple_2k_h.py", "symbol": "solve_simple_2kh_placements"},
"supports": ["mechanism.planetary.simple_2k_h"],
"limitations": [],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.layout.simple_2kh_cascade",
"version": "1.0.0",
"kind": "layout_solver",
"implementation": {"mode": "legacy_binding", "source_path": "src/placement/simple_2k_h_cascade.py", "symbol": "solve_cascade_placements"},
"supports": ["mechanism.planetary.simple_2k_h_cascade", "composition.coaxial_serial.two_simple_2kh"],
"limitations": ["Exactly two stages are supported."],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.layout.ferguson_wolfrom",
"version": "1.0.0",
"kind": "layout_solver",
"implementation": {"mode": "legacy_binding", "source_path": "src/placement/ferguson_wolfrom.py", "symbol": "solve_ferguson_wolfrom_placements"},
"supports": ["mechanism.planetary.ferguson_wolfrom"],
"limitations": [],
"maturity": "geometry_validated"
},
{
"schema_version": "1.0",
"id": "capability.cad.simplecad_parts",
"version": "1.0.0",
"kind": "component_cad_generator",
"implementation": {"mode": "legacy_binding", "source_path": "src/cad/simplecad_parts.py", "symbol": null},
"supports": ["component.gear.cylindrical.external", "component.gear.cylindrical.internal", "component.carrier.planetary"],
"limitations": ["Component generation is orchestrated by family-specific assembly generators."],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.cad.simple_2kh",
"version": "1.0.0",
"kind": "assembly_cad_generator",
"implementation": {"mode": "legacy_binding", "source_path": "src/cad/simplecad_generator.py", "symbol": "generate_simplecad"},
"supports": ["mechanism.planetary.simple_2k_h"],
"limitations": ["Spur and helical tooth forms only."],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.cad.simple_2kh_cascade",
"version": "1.0.0",
"kind": "assembly_cad_generator",
"implementation": {"mode": "legacy_binding", "source_path": "src/cad/simplecad_cascade_generator.py", "symbol": "generate_simplecad_cascade"},
"supports": ["mechanism.planetary.simple_2k_h_cascade", "composition.coaxial_serial.two_simple_2kh"],
"limitations": ["Exactly two stages are supported."],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.cad.ferguson_wolfrom",
"version": "1.0.0",
"kind": "assembly_cad_generator",
"implementation": {"mode": "legacy_binding", "source_path": "src/cad/simplecad_ferguson_wolfrom_generator.py", "symbol": "generate_simplecad_ferguson_wolfrom"},
"supports": ["mechanism.planetary.ferguson_wolfrom"],
"limitations": ["Kinematic-demo detail only; not supported by the coaxial joint-module adapter."],
"maturity": "geometry_validated"
},
{
"schema_version": "1.0",
"id": "capability.validation.external_mesh",
"version": "1.0.0",
"kind": "relation_validator",
"implementation": {"mode": "legacy_binding", "source_path": "src/relation_validators/mesh.py", "symbol": "ExternalMeshValidator"},
"supports": ["relation.mesh.external_cylindrical"],
"limitations": [],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.validation.internal_mesh",
"version": "1.0.0",
"kind": "relation_validator",
"implementation": {"mode": "legacy_binding", "source_path": "src/relation_validators/mesh.py", "symbol": "InternalMeshValidator"},
"supports": ["relation.mesh.internal_cylindrical"],
"limitations": [],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.validation.revolute_joint",
"version": "1.0.0",
"kind": "relation_validator",
"implementation": {"mode": "legacy_binding", "source_path": "src/relation_validators/joints.py", "symbol": "RevoluteJointValidator"},
"supports": ["relation.joint.revolute"],
"limitations": [],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.validation.fixed",
"version": "1.0.0",
"kind": "relation_validator",
"implementation": {"mode": "legacy_binding", "source_path": "src/relation_validators/joints.py", "symbol": "FixedValidator"},
"supports": ["relation.joint.fixed", "component.reference.housing"],
"limitations": [],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.validation.rigid",
"version": "1.0.0",
"kind": "relation_validator",
"implementation": {"mode": "legacy_binding", "source_path": "src/relation_validators/joints.py", "symbol": "RigidValidator"},
"supports": ["relation.joint.rigid"],
"limitations": [],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.validation.coaxial",
"version": "1.0.0",
"kind": "relation_validator",
"implementation": {"mode": "legacy_binding", "source_path": "src/relation_validators/joints.py", "symbol": "CoaxialValidator"},
"supports": ["relation.alignment.coaxial", "component.reference.main_axis"],
"limitations": ["Current topology validator assumes the project main Z axis."],
"maturity": "production"
},
{
"schema_version": "1.0",
"id": "capability.validation.reducer",
"version": "1.0.0",
"kind": "validation_pipeline",
"implementation": {"mode": "legacy_binding", "source_path": "src/validators.py", "symbol": "validate_from_files"},
"supports": ["mechanism.planetary.simple_2k_h", "mechanism.planetary.simple_2k_h_cascade", "mechanism.planetary.ferguson_wolfrom"],
"limitations": ["Validation coverage remains family dependent."],
"maturity": "production"
}
]
}
+23
View File
@@ -0,0 +1,23 @@
{
"schema_version": "1.0",
"id": "catalog.mechanical_transmission",
"version": "0.1.0",
"mode": "detached_read_only",
"integration_state": "not_connected",
"default_enabled": false,
"scope": "general_mechanical_transmission",
"initial_domain": "planetary_reducers",
"entry_roots": [
"components",
"relations",
"mechanisms",
"compositions",
"capabilities",
"evidence"
],
"stable_kernel": {
"path": "src",
"may_import_knowledge": false,
"knowledge_may_reference_kernel": true
}
}
@@ -0,0 +1,29 @@
{
"schema_version": "1.0",
"id": "component.carrier.planetary",
"version": "1.0.0",
"kind": "carrier",
"virtual": false,
"roles": ["carrier", "static_carrier", "output_carrier"],
"parameters": {
"planet_count": {"type": "integer", "minimum": 1},
"planet_orbit_radius_mm": {"type": "number", "exclusive_minimum": 0},
"plate_thickness_mm": {"type": "number", "exclusive_minimum": 0}
},
"ports": [
{"id": "rotation_axis", "type": "port.rotation.coaxial", "multiplicity": "one"},
{"id": "planet_pin_joint", "type": "port.joint.revolute", "multiplicity": "many"},
{"id": "stationary_mount", "type": "port.mount.stationary", "multiplicity": "one"}
],
"capability_refs": [
"capability.cad.simplecad_parts",
"capability.validation.revolute_joint"
],
"applicability": {
"current_mechanisms": [
"mechanism.planetary.simple_2k_h",
"mechanism.planetary.ferguson_wolfrom"
]
},
"maturity": "production"
}
@@ -0,0 +1,32 @@
{
"schema_version": "1.0",
"id": "component.gear.cylindrical.external",
"version": "1.0.0",
"kind": "gear",
"virtual": false,
"roles": ["sun", "planet", "parallel_axis_gear"],
"parameters": {
"teeth": {"type": "integer", "minimum_exclusive": 17},
"module_mm": {"type": "number", "exclusive_minimum": 0},
"pressure_angle_deg": {"type": "number", "exclusive_minimum": 0, "maximum": 45},
"face_width_mm": {"type": "number", "exclusive_minimum": 0},
"tooth_form": {"enum": ["spur", "helical"]},
"helix_angle_deg": {"type": "number", "minimum": 0, "maximum": 35},
"helix_hand": {"enum": ["left", "right"]}
},
"ports": [
{"id": "rotation_axis", "type": "port.rotation.coaxial", "multiplicity": "one"},
{"id": "external_teeth", "type": "port.mesh.external_cylindrical", "multiplicity": "many"},
{"id": "journal_rotation", "type": "port.joint.revolute", "multiplicity": "one"},
{"id": "radial_support", "type": "port.support.radial", "multiplicity": "many"}
],
"capability_refs": [
"capability.cad.simplecad_parts",
"capability.validation.external_mesh"
],
"applicability": {
"current_project_tooth_forms": ["spur", "helical"],
"note": "Sun and planet are mechanism roles, not separate component families."
},
"maturity": "production"
}
@@ -0,0 +1,31 @@
{
"schema_version": "1.0",
"id": "component.gear.cylindrical.internal",
"version": "1.0.0",
"kind": "gear",
"virtual": false,
"roles": ["ring", "internal_gear"],
"parameters": {
"teeth": {"type": "integer", "minimum_exclusive": 17},
"module_mm": {"type": "number", "exclusive_minimum": 0},
"pressure_angle_deg": {"type": "number", "exclusive_minimum": 0, "maximum": 45},
"face_width_mm": {"type": "number", "exclusive_minimum": 0},
"rim_thickness_mm": {"type": "number", "exclusive_minimum": 0},
"tooth_form": {"enum": ["spur", "helical"]},
"helix_angle_deg": {"type": "number", "minimum": 0, "maximum": 35},
"helix_hand": {"enum": ["left", "right"]}
},
"ports": [
{"id": "rotation_axis", "type": "port.rotation.coaxial", "multiplicity": "one"},
{"id": "internal_teeth", "type": "port.mesh.internal_cylindrical", "multiplicity": "many"},
{"id": "housing_mount", "type": "port.mount.stationary", "multiplicity": "one"}
],
"capability_refs": [
"capability.cad.simplecad_parts",
"capability.validation.internal_mesh"
],
"applicability": {
"current_project_tooth_forms": ["spur", "helical"]
},
"maturity": "production"
}
@@ -0,0 +1,18 @@
{
"schema_version": "1.0",
"id": "component.reference.housing",
"version": "1.0.0",
"kind": "housing",
"virtual": true,
"roles": ["housing", "ground_reference"],
"parameters": {},
"ports": [
{"id": "stationary_mount", "type": "port.mount.stationary", "multiplicity": "many"},
{"id": "main_axis", "type": "port.reference.axis", "multiplicity": "one"}
],
"capability_refs": ["capability.validation.fixed"],
"applicability": {
"note": "Topology reference; physical housing details are represented by industrial assembly knowledge."
},
"maturity": "production"
}
@@ -0,0 +1,17 @@
{
"schema_version": "1.0",
"id": "component.reference.main_axis",
"version": "1.0.0",
"kind": "axis",
"virtual": true,
"roles": ["main_axis"],
"parameters": {},
"ports": [
{"id": "axis", "type": "port.reference.axis", "multiplicity": "many"}
],
"capability_refs": ["capability.validation.coaxial"],
"applicability": {
"default_axis_xyz": [0.0, 0.0, 1.0]
},
"maturity": "production"
}
@@ -0,0 +1,30 @@
{
"schema_version": "1.0",
"id": "composition.coaxial_serial.two_simple_2kh",
"version": "1.0.0",
"composition_type": "serial_coaxial",
"source_port_types": ["port.rotation.coaxial"],
"target_port_types": ["port.rotation.coaxial"],
"connection_relation": "relation.joint.rigid",
"allowed_role_pairs": [
{"source": "configured_stage_output", "target": "configured_next_stage_input"}
],
"constraint_rules": [
"exactly_two_simple_2k_h_stages",
"axes_coaxial",
"no_conflicting_fixed_members",
"rigid_endpoint_speeds_equal",
"axial_stack_clearance"
],
"capability_refs": [
"capability.parameter_solver.simple_2kh_cascade",
"capability.layout.simple_2kh_cascade",
"capability.cad.simple_2kh_cascade"
],
"execution_support": {
"level": "full",
"legacy_family": "simple_2k_h_cascade",
"generic_n_stage": false
},
"maturity": "production"
}
@@ -0,0 +1,26 @@
{
"schema_version": "1.0",
"id": "evidence.legacy_baseline.2026_08_13",
"version": "1.0.0",
"evidence_type": "source_and_regression_baseline",
"subject_refs": [
"mechanism.planetary.simple_2k_h",
"mechanism.planetary.simple_2k_h_cascade",
"mechanism.planetary.ferguson_wolfrom"
],
"artifacts": [
{"path": "src/configurations/simple_2k_h/topology.template.json", "sha256": "a444fb2ea3ef16929f99a2f2a8cbd05442cedd91e7e3e67ec95cf344e3e282c3"},
{"path": "src/configurations/simple_2k_h_cascade/topology.template.json", "sha256": "308f60804aa1da2be791ce3e90e3d93c9a5f1f5cacdd0379818e908cbefb9613"},
{"path": "src/configurations/ferguson_wolfrom/topology.template.json", "sha256": "82a19e71d776628c79d9d16871d7e7a07f093ecaff84d1114d77f620d3ae074c"},
{"path": "tests/test_topology_and_kinematics.py", "purpose": "topology, parameter, and graph-kinematics regression"},
{"path": "tests/test_cad_integration.py", "purpose": "real CAD integration regression"},
{"path": "tests/test_validation.py", "purpose": "relation and physical validation regression"},
{"path": "tests/test_joint_module.py", "purpose": "joint-module and URDF regression"}
],
"claims": [
"The knowledge directory is detached from the existing src execution path.",
"The initial knowledge entries describe existing project behavior and do not add a new executable topology.",
"Legacy topology hashes must remain stable unless an intentional baseline update is reviewed."
],
"maturity": "production"
}
@@ -0,0 +1,67 @@
{
"schema_version": "1.0",
"id": "mechanism.planetary.ferguson_wolfrom",
"version": "1.0.0",
"legacy_family": "ferguson_wolfrom",
"description": "Ferguson-Wolfrom mechanical-paradox planetary reducer represented by two coupled sun/ring/planet sets.",
"members": [
{"id": "sun1", "component_ref": "component.gear.cylindrical.external", "role": "sun_fixed_side", "repeat": "single"},
{"id": "sun2", "component_ref": "component.gear.cylindrical.external", "role": "sun_output_side", "repeat": "single"},
{"id": "planet1", "component_ref": "component.gear.cylindrical.external", "role": "input_planet", "repeat": "planet_count"},
{"id": "planet2", "component_ref": "component.gear.cylindrical.external", "role": "output_planet", "repeat": "planet_count"},
{"id": "ring1", "component_ref": "component.gear.cylindrical.internal", "role": "ring_fixed_side", "repeat": "single"},
{"id": "ring2", "component_ref": "component.gear.cylindrical.internal", "role": "ring_output_side", "repeat": "single"},
{"id": "static_carrier", "component_ref": "component.carrier.planetary", "role": "static_carrier", "repeat": "single"},
{"id": "output_carrier", "component_ref": "component.carrier.planetary", "role": "output_carrier", "repeat": "single"},
{"id": "housing", "component_ref": "component.reference.housing", "role": "housing", "repeat": "single"},
{"id": "main_axis", "component_ref": "component.reference.main_axis", "role": "main_axis", "repeat": "single"}
],
"relations": [
{"id": "sun1_planet1", "source": "sun1", "target": "planet1", "relation_ref": "relation.mesh.external_cylindrical", "carrier_ref": "static_carrier"},
{"id": "ring1_planet1", "source": "ring1", "target": "planet1", "relation_ref": "relation.mesh.internal_cylindrical", "carrier_ref": "static_carrier"},
{"id": "sun2_planet2", "source": "sun2", "target": "planet2", "relation_ref": "relation.mesh.external_cylindrical", "carrier_ref": "output_carrier"},
{"id": "ring2_planet2", "source": "ring2", "target": "planet2", "relation_ref": "relation.mesh.internal_cylindrical", "carrier_ref": "output_carrier"},
{"id": "planet1_carrier", "source": "planet1", "target": "static_carrier", "relation_ref": "relation.joint.revolute"},
{"id": "planet2_carrier", "source": "planet2", "target": "output_carrier", "relation_ref": "relation.joint.revolute"},
{"id": "sun_pair", "source": "sun1", "target": "sun2", "relation_ref": "relation.joint.rigid"},
{"id": "ring_pair", "source": "ring1", "target": "ring2", "relation_ref": "relation.joint.rigid"},
{"id": "sun1_axis", "source": "sun1", "target": "main_axis", "relation_ref": "relation.alignment.coaxial"},
{"id": "sun2_axis", "source": "sun2", "target": "main_axis", "relation_ref": "relation.alignment.coaxial"},
{"id": "ring1_axis", "source": "ring1", "target": "main_axis", "relation_ref": "relation.alignment.coaxial"},
{"id": "ring2_axis", "source": "ring2", "target": "main_axis", "relation_ref": "relation.alignment.coaxial"},
{"id": "static_carrier_axis", "source": "static_carrier", "target": "main_axis", "relation_ref": "relation.alignment.coaxial"},
{"id": "output_carrier_axis", "source": "output_carrier", "target": "main_axis", "relation_ref": "relation.alignment.coaxial"},
{"id": "static_carrier_ground", "source": "static_carrier", "target": "housing", "relation_ref": "relation.joint.fixed"}
],
"external_ports": [
{"id": "input_rotation", "member": "planet1", "type": "port.joint.revolute"},
{"id": "output_rotation", "member": "output_carrier", "type": "port.rotation.coaxial"},
{"id": "housing_mount", "member": "housing", "type": "port.mount.stationary"}
],
"boundary_profiles": [
{"id": "planet1_in_static_carrier_fixed_output_carrier_out", "input": "planet1", "fixed": ["static_carrier"], "output": "output_carrier", "production_evidence": true}
],
"constraint_rules": [
"paired_suns_rigid",
"paired_rings_rigid",
"required_tooth_counts_explicit",
"minimum_teeth_exclusive_17",
"graph_kinematics_residual_within_tolerance"
],
"capability_refs": [
"capability.parameter_solver.ferguson_wolfrom",
"capability.kinematics.graph",
"capability.layout.ferguson_wolfrom",
"capability.cad.ferguson_wolfrom",
"capability.validation.reducer"
],
"source_topology": {
"path": "src/configurations/ferguson_wolfrom/topology.template.json",
"sha256": "82a19e71d776628c79d9d16871d7e7a07f093ecaff84d1114d77f620d3ae074c"
},
"limitations": [
"Current implementation supports kinematic_demo detail level only.",
"The eccentric planet-pin input is not compatible with the current coaxial joint-module adapter."
],
"maturity": "geometry_validated"
}
@@ -0,0 +1,60 @@
{
"schema_version": "1.0",
"id": "mechanism.planetary.simple_2k_h",
"version": "1.0.0",
"legacy_family": "simple_2k_h",
"description": "Single simple 2K-H planetary cell with interchangeable input, fixed, and output boundary members.",
"members": [
{"id": "sun", "component_ref": "component.gear.cylindrical.external", "role": "sun", "repeat": "single"},
{"id": "planet", "component_ref": "component.gear.cylindrical.external", "role": "planet", "repeat": "planet_count"},
{"id": "ring", "component_ref": "component.gear.cylindrical.internal", "role": "ring", "repeat": "single"},
{"id": "carrier", "component_ref": "component.carrier.planetary", "role": "carrier", "repeat": "single"},
{"id": "housing", "component_ref": "component.reference.housing", "role": "housing", "repeat": "single"},
{"id": "main_axis", "component_ref": "component.reference.main_axis", "role": "main_axis", "repeat": "single"}
],
"relations": [
{"id": "sun_planet", "source": "sun", "target": "planet", "relation_ref": "relation.mesh.external_cylindrical", "carrier_ref": "carrier"},
{"id": "ring_planet", "source": "ring", "target": "planet", "relation_ref": "relation.mesh.internal_cylindrical", "carrier_ref": "carrier"},
{"id": "planet_carrier", "source": "planet", "target": "carrier", "relation_ref": "relation.joint.revolute"},
{"id": "sun_axis", "source": "sun", "target": "main_axis", "relation_ref": "relation.alignment.coaxial"},
{"id": "ring_axis", "source": "ring", "target": "main_axis", "relation_ref": "relation.alignment.coaxial"},
{"id": "carrier_axis", "source": "carrier", "target": "main_axis", "relation_ref": "relation.alignment.coaxial"},
{"id": "fixed_member_housing", "source": "ring", "target": "housing", "relation_ref": "relation.joint.fixed", "boundary_substitutable": true}
],
"external_ports": [
{"id": "sun_rotation", "member": "sun", "type": "port.rotation.coaxial"},
{"id": "ring_rotation", "member": "ring", "type": "port.rotation.coaxial"},
{"id": "carrier_rotation", "member": "carrier", "type": "port.rotation.coaxial"},
{"id": "housing_mount", "member": "housing", "type": "port.mount.stationary"}
],
"boundary_profiles": [
{"id": "sun_in_ring_fixed_carrier_out", "input": "sun", "fixed": ["ring"], "output": "carrier", "production_evidence": true},
{"id": "ring_in_sun_fixed_carrier_out", "input": "ring", "fixed": ["sun"], "output": "carrier", "production_evidence": true},
{"id": "carrier_in_ring_fixed_sun_out", "input": "carrier", "fixed": ["ring"], "output": "sun", "production_evidence": true},
{"id": "sun_in_carrier_fixed_ring_out", "input": "sun", "fixed": ["carrier"], "output": "ring", "production_evidence": true}
],
"constraint_rules": [
"z_ring = z_sun + 2*z_planet",
"(z_sun + z_ring) % planet_count = 0",
"common_module",
"common_pressure_angle",
"planet_neighbor_clearance",
"maximum_outer_diameter"
],
"capability_refs": [
"capability.parameter_solver.simple_2kh",
"capability.kinematics.graph",
"capability.layout.simple_2kh",
"capability.cad.simple_2kh",
"capability.validation.reducer"
],
"source_topology": {
"path": "src/configurations/simple_2k_h/topology.template.json",
"sha256": "a444fb2ea3ef16929f99a2f2a8cbd05442cedd91e7e3e67ec95cf344e3e282c3"
},
"limitations": [
"Current CAD supports spur and helical tooth forms.",
"Industrial-core generation currently restricts boundary variants."
],
"maturity": "production"
}
@@ -0,0 +1,47 @@
{
"schema_version": "1.0",
"id": "mechanism.planetary.simple_2k_h_cascade",
"version": "1.0.0",
"legacy_family": "simple_2k_h_cascade",
"description": "Exactly two simple 2K-H stages connected by a rigid stage-output to next-stage-input coupling.",
"members": [
{"id": "stage_1", "mechanism_ref": "mechanism.planetary.simple_2k_h", "role": "first_stage", "repeat": "single"},
{"id": "stage_2", "mechanism_ref": "mechanism.planetary.simple_2k_h", "role": "second_stage", "repeat": "single"},
{"id": "housing", "component_ref": "component.reference.housing", "role": "housing", "repeat": "single"},
{"id": "main_axis", "component_ref": "component.reference.main_axis", "role": "main_axis", "repeat": "single"}
],
"relations": [
{"id": "stage_output_to_next_input", "source": "stage_1", "target": "stage_2", "relation_ref": "relation.joint.rigid", "composition_ref": "composition.coaxial_serial.two_simple_2kh"}
],
"external_ports": [
{"id": "input_rotation", "member": "stage_1", "type": "port.rotation.coaxial"},
{"id": "output_rotation", "member": "stage_2", "type": "port.rotation.coaxial"},
{"id": "housing_mount", "member": "housing", "type": "port.mount.stationary"}
],
"boundary_profiles": [
{"id": "per_stage_boundaries", "input": "stage_1.configured_input", "fixed": ["stage_1.configured_fixed", "stage_2.configured_fixed"], "output": "stage_2.configured_output", "production_evidence": true}
],
"constraint_rules": [
"exactly_two_stages",
"each_stage_is_simple_2k_h",
"stage_output_rigid_to_next_stage_input",
"common_main_axis",
"axial_stack_clearance"
],
"capability_refs": [
"capability.parameter_solver.simple_2kh_cascade",
"capability.kinematics.graph",
"capability.layout.simple_2kh_cascade",
"capability.cad.simple_2kh_cascade",
"capability.validation.reducer"
],
"source_topology": {
"path": "src/configurations/simple_2k_h_cascade/topology.template.json",
"sha256": "308f60804aa1da2be791ce3e90e3d93c9a5f1f5cacdd0379818e908cbefb9613"
},
"limitations": [
"This is a fixed two-stage implementation, not a generic N-stage composer.",
"Only the existing rigid output-to-input composition is production supported."
],
"maturity": "production"
}
@@ -0,0 +1,15 @@
{
"schema_version": "1.0",
"id": "ontology.maturity_levels",
"version": "1.0.0",
"ordered_levels": [
"declared",
"topology_validated",
"kinematically_validated",
"parameter_solved",
"cad_generated",
"geometry_validated",
"production"
],
"rule": "An entry may only claim the highest stage supported by recorded evidence and executable capability bindings."
}
@@ -0,0 +1,15 @@
{
"schema_version": "1.0",
"id": "ontology.port_types",
"version": "1.0.0",
"entries": [
{"id": "port.rotation.coaxial", "domain": "rotation", "axis_relation": "coaxial"},
{"id": "port.mesh.external_cylindrical", "domain": "gear_mesh", "axis_relation": "parallel"},
{"id": "port.mesh.internal_cylindrical", "domain": "gear_mesh", "axis_relation": "parallel"},
{"id": "port.joint.revolute", "domain": "joint", "axis_relation": "coaxial_local"},
{"id": "port.mount.stationary", "domain": "mount", "axis_relation": "not_applicable"},
{"id": "port.reference.axis", "domain": "reference", "axis_relation": "self"},
{"id": "port.support.radial", "domain": "support", "axis_relation": "coaxial"},
{"id": "port.support.axial", "domain": "support", "axis_relation": "coaxial"}
]
}
@@ -0,0 +1,13 @@
{
"schema_version": "1.0",
"id": "ontology.relation_types",
"version": "1.0.0",
"entries": [
{"type": "external_mesh", "knowledge_id": "relation.mesh.external_cylindrical"},
{"type": "internal_mesh", "knowledge_id": "relation.mesh.internal_cylindrical"},
{"type": "revolute_joint", "knowledge_id": "relation.joint.revolute"},
{"type": "coaxial", "knowledge_id": "relation.alignment.coaxial"},
{"type": "fixed", "knowledge_id": "relation.joint.fixed"},
{"type": "rigid", "knowledge_id": "relation.joint.rigid"}
]
}
+17
View File
@@ -0,0 +1,17 @@
{
"schema_version": "1.0",
"id": "ontology.units",
"version": "1.0.0",
"canonical": {
"length": "mm",
"angle": "deg",
"angular_speed": "rad_per_s",
"torque": "N_m",
"force": "N",
"mass": "kg"
},
"coordinate_convention": {
"handedness": "right_handed",
"default_rotation_axis": [0.0, 0.0, 1.0]
}
}
@@ -0,0 +1,15 @@
{
"schema_version": "1.0",
"id": "relation.alignment.coaxial",
"version": "1.0.0",
"relation_type": "coaxial",
"endpoint_rules": {
"source_port": "port.rotation.coaxial",
"target_port": "port.reference.axis",
"axis_relation": "coincident"
},
"constraint_rules": ["axes_parallel", "axis_distance_within_tolerance"],
"kinematic_effect": null,
"capability_refs": ["capability.validation.coaxial"],
"maturity": "production"
}
@@ -0,0 +1,18 @@
{
"schema_version": "1.0",
"id": "relation.joint.fixed",
"version": "1.0.0",
"relation_type": "fixed",
"endpoint_rules": {
"source_port": "port.rotation.coaxial",
"target_port": "port.mount.stationary",
"axis_relation": "mechanism_defined"
},
"constraint_rules": ["one_endpoint_is_grounded", "fixed_speed_is_zero"],
"kinematic_effect": {"equation": "omega_source = omega_target = 0"},
"capability_refs": [
"capability.kinematics.graph",
"capability.validation.fixed"
],
"maturity": "production"
}
@@ -0,0 +1,15 @@
{
"schema_version": "1.0",
"id": "relation.joint.revolute",
"version": "1.0.0",
"relation_type": "revolute_joint",
"endpoint_rules": {
"source_port": "port.joint.revolute",
"target_port": "port.joint.revolute",
"axis_relation": "coaxial_local"
},
"constraint_rules": ["joint_axis_defined", "carrier_pin_orbit_matches_layout"],
"kinematic_effect": null,
"capability_refs": ["capability.validation.revolute_joint"],
"maturity": "production"
}
@@ -0,0 +1,18 @@
{
"schema_version": "1.0",
"id": "relation.joint.rigid",
"version": "1.0.0",
"relation_type": "rigid",
"endpoint_rules": {
"source_port": "port.rotation.coaxial",
"target_port": "port.rotation.coaxial",
"axis_relation": "coaxial"
},
"constraint_rules": ["axes_coaxial", "angular_speeds_equal", "torque_capacity_compatible"],
"kinematic_effect": {"equation": "omega_source = omega_target"},
"capability_refs": [
"capability.kinematics.graph",
"capability.validation.rigid"
],
"maturity": "production"
}
@@ -0,0 +1,26 @@
{
"schema_version": "1.0",
"id": "relation.mesh.external_cylindrical",
"version": "1.0.0",
"relation_type": "external_mesh",
"endpoint_rules": {
"source_port": "port.mesh.external_cylindrical",
"target_port": "port.mesh.external_cylindrical",
"axis_relation": "parallel"
},
"constraint_rules": [
"equal_module",
"equal_pressure_angle",
"equal_tooth_form",
"spur_or_opposite_helical_hand",
"center_distance_equals_pitch_radius_sum"
],
"kinematic_effect": {
"equation": "(omega_source-omega_carrier)*z_source + (omega_target-omega_carrier)*z_target = 0"
},
"capability_refs": [
"capability.kinematics.graph",
"capability.validation.external_mesh"
],
"maturity": "production"
}
@@ -0,0 +1,26 @@
{
"schema_version": "1.0",
"id": "relation.mesh.internal_cylindrical",
"version": "1.0.0",
"relation_type": "internal_mesh",
"endpoint_rules": {
"source_port": "port.mesh.internal_cylindrical",
"target_port": "port.mesh.external_cylindrical",
"axis_relation": "parallel"
},
"constraint_rules": [
"equal_module",
"equal_pressure_angle",
"equal_tooth_form",
"spur_or_same_helical_hand",
"center_distance_equals_pitch_radius_difference"
],
"kinematic_effect": {
"equation": "(omega_source-omega_carrier)*z_source - (omega_target-omega_carrier)*z_target = 0"
},
"capability_refs": [
"capability.kinematics.graph",
"capability.validation.internal_mesh"
],
"maturity": "production"
}
@@ -0,0 +1,27 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "kb://schemas/capability.schema.json",
"title": "Executable capability binding",
"type": "object",
"required": ["schema_version", "id", "version", "kind", "implementation", "supports", "maturity"],
"properties": {
"schema_version": {"const": "1.0"},
"id": {"type": "string", "pattern": "^capability\\."},
"version": {"type": "string"},
"kind": {"type": "string"},
"implementation": {
"type": "object",
"required": ["mode", "source_path"],
"properties": {
"mode": {"enum": ["legacy_binding", "declarative"]},
"source_path": {"type": "string"},
"symbol": {"type": ["string", "null"]}
},
"additionalProperties": false
},
"supports": {"type": "array", "items": {"type": "string"}},
"limitations": {"type": "array", "items": {"type": "string"}},
"maturity": {"type": "string"}
},
"additionalProperties": false
}
@@ -0,0 +1,33 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "kb://schemas/component.schema.json",
"title": "Mechanical component knowledge entry",
"type": "object",
"required": ["schema_version", "id", "version", "kind", "parameters", "ports", "capability_refs", "maturity"],
"properties": {
"schema_version": {"const": "1.0"},
"id": {"type": "string", "pattern": "^component\\."},
"version": {"type": "string"},
"kind": {"type": "string"},
"virtual": {"type": "boolean", "default": false},
"roles": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"parameters": {"type": "object"},
"ports": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "type"],
"properties": {
"id": {"type": "string"},
"type": {"type": "string"},
"multiplicity": {"type": "string", "enum": ["one", "many"]}
},
"additionalProperties": false
}
},
"capability_refs": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"applicability": {"type": "object"},
"maturity": {"type": "string"}
},
"additionalProperties": false
}
@@ -0,0 +1,22 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "kb://schemas/composition.schema.json",
"title": "Mechanism composition knowledge entry",
"type": "object",
"required": ["schema_version", "id", "version", "composition_type", "source_port_types", "target_port_types", "connection_relation", "constraint_rules", "capability_refs", "execution_support", "maturity"],
"properties": {
"schema_version": {"const": "1.0"},
"id": {"type": "string", "pattern": "^composition\\."},
"version": {"type": "string"},
"composition_type": {"type": "string"},
"source_port_types": {"type": "array", "items": {"type": "string"}},
"target_port_types": {"type": "array", "items": {"type": "string"}},
"connection_relation": {"type": "string"},
"allowed_role_pairs": {"type": "array", "items": {"type": "object"}},
"constraint_rules": {"type": "array", "items": {"type": "string"}},
"capability_refs": {"type": "array", "items": {"type": "string"}},
"execution_support": {"type": "object"},
"maturity": {"type": "string"}
},
"additionalProperties": false
}
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "kb://schemas/evidence.schema.json",
"title": "Knowledge evidence entry",
"type": "object",
"required": ["schema_version", "id", "version", "evidence_type", "subject_refs", "artifacts", "maturity"],
"properties": {
"schema_version": {"const": "1.0"},
"id": {"type": "string", "pattern": "^evidence\\."},
"version": {"type": "string"},
"evidence_type": {"type": "string"},
"subject_refs": {"type": "array", "items": {"type": "string"}},
"artifacts": {"type": "array", "items": {"type": "object"}},
"claims": {"type": "array", "items": {"type": "string"}},
"maturity": {"type": "string"}
},
"additionalProperties": false
}
@@ -0,0 +1,24 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "kb://schemas/mechanism.schema.json",
"title": "Mechanism knowledge entry",
"type": "object",
"required": ["schema_version", "id", "version", "legacy_family", "members", "relations", "external_ports", "boundary_profiles", "constraint_rules", "capability_refs", "maturity"],
"properties": {
"schema_version": {"const": "1.0"},
"id": {"type": "string", "pattern": "^mechanism\\."},
"version": {"type": "string"},
"legacy_family": {"type": ["string", "null"]},
"description": {"type": "string"},
"members": {"type": "array", "items": {"type": "object"}},
"relations": {"type": "array", "items": {"type": "object"}},
"external_ports": {"type": "array", "items": {"type": "object"}},
"boundary_profiles": {"type": "array", "items": {"type": "object"}},
"constraint_rules": {"type": "array", "items": {"type": "string"}},
"capability_refs": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"source_topology": {"type": "object"},
"limitations": {"type": "array", "items": {"type": "string"}},
"maturity": {"type": "string"}
},
"additionalProperties": false
}
@@ -0,0 +1,19 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "kb://schemas/relation.schema.json",
"title": "Mechanical relation knowledge entry",
"type": "object",
"required": ["schema_version", "id", "version", "relation_type", "endpoint_rules", "constraint_rules", "capability_refs", "maturity"],
"properties": {
"schema_version": {"const": "1.0"},
"id": {"type": "string", "pattern": "^relation\\."},
"version": {"type": "string"},
"relation_type": {"type": "string"},
"endpoint_rules": {"type": "object"},
"constraint_rules": {"type": "array", "items": {"type": "string"}},
"kinematic_effect": {"type": ["object", "null"]},
"capability_refs": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
"maturity": {"type": "string"}
},
"additionalProperties": false
}
@@ -0,0 +1,289 @@
#!/usr/bin/env python3
"""Read-only consistency validator for the detached knowledge base."""
from __future__ import annotations
import ast
import hashlib
import json
import sys
from pathlib import Path
from typing import Any
KNOWLEDGE_ROOT = Path(__file__).resolve().parents[1]
BACKEND_ROOT = KNOWLEDGE_ROOT.parent
ENTRY_DIRS = {
"component": KNOWLEDGE_ROOT / "components",
"relation": KNOWLEDGE_ROOT / "relations",
"mechanism": KNOWLEDGE_ROOT / "mechanisms",
"composition": KNOWLEDGE_ROOT / "compositions",
"evidence": KNOWLEDGE_ROOT / "evidence",
}
REQUIRED_FIELDS = {
"component": {"schema_version", "id", "version", "kind", "parameters", "ports", "capability_refs", "maturity"},
"relation": {"schema_version", "id", "version", "relation_type", "endpoint_rules", "constraint_rules", "capability_refs", "maturity"},
"mechanism": {"schema_version", "id", "version", "legacy_family", "members", "relations", "external_ports", "boundary_profiles", "constraint_rules", "capability_refs", "maturity"},
"composition": {"schema_version", "id", "version", "composition_type", "source_port_types", "target_port_types", "connection_relation", "constraint_rules", "capability_refs", "execution_support", "maturity"},
"capability": {"schema_version", "id", "version", "kind", "implementation", "supports", "maturity"},
"evidence": {"schema_version", "id", "version", "evidence_type", "subject_refs", "artifacts", "maturity"},
}
def load_json(path: Path, errors: list[str]) -> Any | None:
try:
return json.loads(path.read_text(encoding="utf-8"))
except Exception as exc: # noqa: BLE001
errors.append(f"invalid_json:{path.relative_to(BACKEND_ROOT)}:{exc}")
return None
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def collect_entries(errors: list[str]) -> tuple[dict[str, dict[str, Any]], dict[str, str]]:
entries: dict[str, dict[str, Any]] = {}
kinds: dict[str, str] = {}
for kind, root in ENTRY_DIRS.items():
for path in sorted(root.rglob("*.json")):
data = load_json(path, errors)
if not isinstance(data, dict):
continue
entry_id = data.get("id")
if not isinstance(entry_id, str):
errors.append(f"missing_id:{path.relative_to(BACKEND_ROOT)}")
continue
if entry_id in entries:
errors.append(f"duplicate_id:{entry_id}")
continue
entries[entry_id] = data
kinds[entry_id] = kind
capability_registry_path = KNOWLEDGE_ROOT / "capabilities" / "registry.json"
registry = load_json(capability_registry_path, errors)
if isinstance(registry, dict):
for data in registry.get("entries", []):
if not isinstance(data, dict) or not isinstance(data.get("id"), str):
errors.append("invalid_capability_registry_entry")
continue
entry_id = data["id"]
if entry_id in entries:
errors.append(f"duplicate_id:{entry_id}")
continue
entries[entry_id] = data
kinds[entry_id] = "capability"
return entries, kinds
def validate_required_fields(
entries: dict[str, dict[str, Any]],
kinds: dict[str, str],
errors: list[str],
) -> None:
for entry_id, entry in entries.items():
kind = kinds[entry_id]
missing = sorted(REQUIRED_FIELDS[kind] - set(entry))
if missing:
errors.append(f"missing_fields:{entry_id}:{','.join(missing)}")
if entry.get("schema_version") != "1.0":
errors.append(f"unsupported_schema_version:{entry_id}:{entry.get('schema_version')}")
expected_prefix = f"{kind}."
if not entry_id.startswith(expected_prefix):
errors.append(f"bad_id_prefix:{entry_id}:expected={expected_prefix}")
def referenced_ids(entry: dict[str, Any], kind: str) -> list[str]:
refs: list[str] = []
refs.extend(entry.get("capability_refs", []))
if kind == "mechanism":
for member in entry.get("members", []):
refs.extend(
value
for key in ("component_ref", "mechanism_ref")
if isinstance((value := member.get(key)), str)
)
for relation in entry.get("relations", []):
refs.extend(
value
for key in ("relation_ref", "composition_ref")
if isinstance((value := relation.get(key)), str)
)
elif kind == "composition":
relation_ref = entry.get("connection_relation")
if isinstance(relation_ref, str):
refs.append(relation_ref)
elif kind == "evidence":
refs.extend(entry.get("subject_refs", []))
return refs
def validate_references(
entries: dict[str, dict[str, Any]],
kinds: dict[str, str],
port_ids: set[str],
errors: list[str],
) -> None:
for entry_id, entry in entries.items():
for reference in referenced_ids(entry, kinds[entry_id]):
if reference not in entries:
errors.append(f"unknown_reference:{entry_id}:{reference}")
for port in entry.get("ports", []):
port_type = port.get("type")
if port_type not in port_ids:
errors.append(f"unknown_port_type:{entry_id}:{port_type}")
for port in entry.get("external_ports", []):
port_type = port.get("type")
if port_type not in port_ids:
errors.append(f"unknown_port_type:{entry_id}:{port_type}")
for key in ("source_port_types", "target_port_types"):
for port_type in entry.get(key, []):
if port_type not in port_ids:
errors.append(f"unknown_port_type:{entry_id}:{port_type}")
def validate_mechanism_graphs(entries: dict[str, dict[str, Any]], errors: list[str]) -> None:
for entry_id, mechanism in entries.items():
if not entry_id.startswith("mechanism."):
continue
member_ids = {member.get("id") for member in mechanism.get("members", [])}
if None in member_ids or len(member_ids) != len(mechanism.get("members", [])):
errors.append(f"invalid_or_duplicate_member:{entry_id}")
relation_ids: set[str] = set()
for relation in mechanism.get("relations", []):
relation_id = relation.get("id")
if relation_id in relation_ids or not relation_id:
errors.append(f"invalid_or_duplicate_relation:{entry_id}:{relation_id}")
relation_ids.add(relation_id)
for endpoint in ("source", "target"):
if relation.get(endpoint) not in member_ids:
errors.append(
f"unknown_relation_endpoint:{entry_id}:{relation_id}:{endpoint}={relation.get(endpoint)}"
)
carrier = relation.get("carrier_ref")
if carrier is not None and carrier not in member_ids:
errors.append(f"unknown_carrier_ref:{entry_id}:{relation_id}:{carrier}")
for port in mechanism.get("external_ports", []):
if port.get("member") not in member_ids:
errors.append(f"unknown_external_port_member:{entry_id}:{port.get('id')}")
for profile in mechanism.get("boundary_profiles", []):
boundary_members = [profile.get("input"), profile.get("output"), *profile.get("fixed", [])]
for member in boundary_members:
root_member = member.split(".", 1)[0] if isinstance(member, str) else member
if root_member not in member_ids:
errors.append(f"unknown_boundary_member:{entry_id}:{profile.get('id')}:{member}")
def validate_capability_sources(entries: dict[str, dict[str, Any]], errors: list[str]) -> None:
for entry_id, entry in entries.items():
if not entry_id.startswith("capability."):
continue
implementation = entry.get("implementation", {})
source_path = implementation.get("source_path")
if not isinstance(source_path, str):
errors.append(f"missing_capability_source:{entry_id}")
continue
source = BACKEND_ROOT / source_path
if not source.is_file():
errors.append(f"missing_capability_source:{entry_id}:{source_path}")
continue
symbol = implementation.get("symbol")
if symbol:
try:
tree = ast.parse(source.read_text(encoding="utf-8"))
except SyntaxError as exc:
errors.append(f"invalid_capability_source_python:{entry_id}:{exc}")
continue
top_level_symbols = {
node.name
for node in tree.body
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef))
}
if symbol not in top_level_symbols:
errors.append(f"missing_capability_symbol:{entry_id}:{source_path}:{symbol}")
def validate_topology_hashes(entries: dict[str, dict[str, Any]], errors: list[str]) -> None:
for entry_id, entry in entries.items():
source_topology = entry.get("source_topology")
if not source_topology:
continue
source = BACKEND_ROOT / source_topology["path"]
if not source.is_file():
errors.append(f"missing_source_topology:{entry_id}:{source_topology['path']}")
continue
actual = sha256_file(source)
if actual != source_topology.get("sha256"):
errors.append(
f"source_topology_hash_mismatch:{entry_id}:expected={source_topology.get('sha256')}:actual={actual}"
)
def validate_detached_boundary(errors: list[str]) -> None:
for path in sorted((BACKEND_ROOT / "src").rglob("*.py")):
try:
tree = ast.parse(path.read_text(encoding="utf-8"))
except SyntaxError:
continue
for node in ast.walk(tree):
module: str | None = None
if isinstance(node, ast.ImportFrom):
module = node.module
elif isinstance(node, ast.Import):
for alias in node.names:
if alias.name == "knowledge" or alias.name.startswith("knowledge."):
errors.append(f"stable_kernel_imports_knowledge:{path.relative_to(BACKEND_ROOT)}:{alias.name}")
if module == "knowledge" or (module and module.startswith("knowledge.")):
errors.append(f"stable_kernel_imports_knowledge:{path.relative_to(BACKEND_ROOT)}:{module}")
def main() -> int:
errors: list[str] = []
for path in sorted(KNOWLEDGE_ROOT.rglob("*.json")):
load_json(path, errors)
catalog = load_json(KNOWLEDGE_ROOT / "catalog.json", errors)
if isinstance(catalog, dict):
if catalog.get("default_enabled") is not False:
errors.append("catalog_default_enabled_must_be_false")
if catalog.get("integration_state") != "not_connected":
errors.append("catalog_integration_state_must_be_not_connected")
entries, kinds = collect_entries(errors)
validate_required_fields(entries, kinds, errors)
ports = load_json(KNOWLEDGE_ROOT / "ontology" / "port_types.json", errors)
port_ids = {
entry["id"]
for entry in (ports or {}).get("entries", [])
if isinstance(entry, dict) and isinstance(entry.get("id"), str)
}
validate_references(entries, kinds, port_ids, errors)
validate_mechanism_graphs(entries, errors)
validate_capability_sources(entries, errors)
validate_topology_hashes(entries, errors)
validate_detached_boundary(errors)
if errors:
print(f"knowledge validation failed: {len(errors)} error(s)", file=sys.stderr)
for error in errors:
print(f"- {error}", file=sys.stderr)
return 1
counts: dict[str, int] = {}
for kind in kinds.values():
counts[kind] = counts.get(kind, 0) + 1
count_text = ", ".join(f"{kind}={counts[kind]}" for kind in sorted(counts))
print(f"knowledge validation passed: {len(entries)} entries ({count_text})")
print("integration_state=not_connected default_enabled=false stable_kernel_imports=0")
return 0
if __name__ == "__main__":
raise SystemExit(main())