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

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.

Raises:



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