Module: KairosMcp::LifecycleHook
- Defined in:
- lib/kairos_mcp/lifecycle_hook.rb
Overview
LifecycleHook protocol (24/7 v0.4 §2.3).
A SkillSet advertises one or more lifecycle hooks in its skillset.json:
{
"name": "daemon_runtime",
"lifecycle_hooks": { "daemon_main": "KairosMcp::SkillSets::DaemonRuntime::MainLoop" }
}
Only one SkillSet may claim a given hook name. Conflicts raise at load time (Conflict) — silent override would violate the audit guarantees of the Bootstrap layer.
Defined Under Namespace
Classes: Conflict, ForbiddenNamespace, InstanceViolation, NotImplementedHook, UnknownClass
Constant Summary collapse
- ALLOWED_NAMESPACES =
Allowlist for class-name resolution (R1 P1, 2-voice security): skillset.json is untrusted input, so ‘Object.const_get` must not instantiate arbitrary classes. Only classes under these namespaces may be bound to a lifecycle hook.
[ 'KairosMcp::SkillSets::' ].freeze
- CLASS_NAME_RE =
Valid Ruby constant path: Foo::Bar::Baz (no leading colons).
/\A[A-Z][A-Za-z0-9_]*(::[A-Z][A-Za-z0-9_]*)*\z/
Class Method Summary collapse
Instance Method Summary collapse
-
#run_main_loop(registry:, signal:) ⇒ Object
Contract an implementing class must fulfill.
Class Method Details
.validate_class_name!(class_name) ⇒ Object
41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/kairos_mcp/lifecycle_hook.rb', line 41 def self.validate_class_name!(class_name) name = class_name.to_s unless name =~ CLASS_NAME_RE raise UnknownClass, "invalid class name: #{class_name.inspect}" end unless ALLOWED_NAMESPACES.any? { |prefix| name.start_with?(prefix) } raise ForbiddenNamespace, "class '#{name}' is not under an allowed namespace " \ "(#{ALLOWED_NAMESPACES.join(', ')})" end name end |
Instance Method Details
#run_main_loop(registry:, signal:) ⇒ Object
Contract an implementing class must fulfill. Bootstrap calls run_main_loop(registry:, signal:) and expects the callee to block until signal.shutdown_requested? is true, then return.
57 58 59 |
# File 'lib/kairos_mcp/lifecycle_hook.rb', line 57 def run_main_loop(registry:, signal:) raise NotImplementedHook, "#{self.class} must implement run_main_loop" end |