Plan: Extract and Generalize Inspect/Kind into termin-inspect¶
Goal¶
Move inspect/kind from termin into an independent library that works with any runtime object (not only components/passes).
Non-Goals¶
- No behavior changes in scene/entity runtime during phase 1.
- No immediate migration of all callsites in one step.
- No temporary fallback layers "just in case".
Current Constraints (from existing code)¶
InspectFieldInfo::actionis effectively component-centric in usage.- C entrypoints are split as
tc_component_inspect_*andtc_pass_inspect_*adapters. - C++ inspect runtime is mixed with component/pass specifics in one place.
- C++ kind runtime currently auto-registers domain handles (
tc_mesh,tc_material) that belong to higher layers.
Target Architecture¶
1) Core package (termin-inspect)¶
Contains only generic reflection/serialization runtime: - Type registry (fields, inheritance, metadata) - Kind registry (C dispatcher + C++ runtime + optional Python runtime extension) - Generic field get/set/action dispatch - JSON/tc_value conversion helpers for inspect path
Core must not depend on: - Scene/entity - Render passes - Mesh/material handles - Editor UI
2) Adapter packages (outside core)¶
Use generic API and provide runtime-specific object mapping:
- termin-scene adapters for component objects
- termin-render adapters for pass objects
- termin Python bindings adapters
These adapters own:
- tc_component_inspect_*
- tc_pass_inspect_*
- Domain kind registrations (tc_mesh, tc_material, etc.)
Generic API Boundary¶
C-level boundary¶
Introduce/normalize a generic object entrypoint:
- tc_inspect_get_object(void* obj, const char* type_name, const char* path)
- tc_inspect_set_object(void* obj, const char* type_name, const char* path, tc_value value, tc_scene_handle scene)
- tc_inspect_action_object(void* obj, const char* type_name, const char* path)
Keep tc_component_inspect_* and tc_pass_inspect_* as thin adapters built outside core.
C++ boundary¶
Generalize action signature:
- from component-specific callback usage
- to std::function<void(void*, const InspectContext&)>
InspectContext keeps optional runtime data (scene handle, user context).
Kind boundary¶
Core KindRegistryCpp should only include primitive/builtin kinds.
Domain kinds must be registered by consumers:
- scene/graphics layer registers mesh/material handles
- other projects can register their own handle types
Migration Stages¶
Stage 0: Bootstrap termin-inspect¶
- Create CMake project and install/export config.
- Add CI skeleton (C/C++ + Python if bindings enabled).
- Add minimal smoke tests for registry create/register/query.
Stage 1: Move pure dispatcher C layer¶
Move from termin/core_c/include/inspect/* and implementation parts that are runtime-agnostic:
- tc_inspect.h/.c
- tc_kind.h/.c
- tc_value JSON helpers used by inspect path
Validation:
- Existing termin compiles by linking termin-inspect.
- C tests for parser, registry, dispatch pass.
Stage 2: Split C++ inspect runtime into core + adapters¶
In current tc_inspect_instance logic:
- Keep only generic registry/vtable logic in core.
- Move component/pass object mapping to adapter files in termin/termin-scene.
Validation: - Same behavior via adapters for component/pass calls. - No component/pass includes inside core files.
Stage 3: Extract kind C++ runtime¶
Move tc_kind_cpp into core, but remove domain auto-registration.
- Keep builtin scalar/vector/quat kinds in core.
- Move mesh/material registrations to graphics/scene integration layer.
Validation: - Core tests for builtin kinds. - Integration tests in consumer for mesh/material kinds.
Stage 4: Python extension layering¶
Split Python bindings: - core python binding (optional): inspect registry + kind registry generic API - consumer bindings: entity/pass-specific adapters
Validation: - Python tests for: - register_python_fields - serialize/deserialize generic objects - custom kind handlers
Stage 5: Replace direct callsites¶
Update termin code to use adapter-owned entrypoints and eliminate internal old paths.
Validation: - editor/project load - C++ module load/reload path still works - python component serialization unchanged
Testing Plan¶
C tests¶
- kind parser (
list[T]) - lang vtable registration and dispatch
- field metadata traversal with inheritance
C++ tests¶
- InspectRegistry field add/get/set with primitive and custom kinds
- generic action callback invocation
- unregister type behavior
Python tests¶
- register_python_fields + serialize/deserialize
- custom python kind handlers
- list kind lazy ensure behavior
Integration tests (consumer side)¶
- component adapter: get/set/action through generic core
- pass adapter: get/set through generic core
- domain handle kinds (
tc_mesh,tc_material) registered externally
Compatibility Strategy¶
- Preserve existing public adapter APIs during migration.
- Keep binary-compatible C symbols where feasible.
- If symbol move is unavoidable, provide deprecation window in adapter layer (not in core).
Risks¶
- Singleton duplication across shared libs if link topology is wrong.
- Python lifetime/refcount errors around action callbacks and class registry.
- Hidden dependencies on scene/render types in inspect/kind callsites.
Definition of Done¶
termin-inspectbuilds and tests independently.terminlinks againsttermin-inspectwithout embedded inspect/kind core sources.- component/pass inspect works via adapters only.
- mesh/material and other domain kinds are registered outside core.
- CI covers C/C++ and Python API smoke for the new package.