Class: Kobako::Transport::Run

Inherits:
Data
  • Object
show all
Defined in:
lib/kobako/transport/run.rb,
sig/kobako/transport/run.rbs

Overview

Host-side value object for a single Sandbox#run invocation (docs/wire-codec.md Invocation channels).

A Run captures the host-layer concept of "a single #run call": the entrypoint constant name plus its positional and keyword arguments. Host pre-flight (entrypoint type / name pattern, forged Handle, kwargs-key type) is enforced at construction so the Value Object is the single source of truth — anything that passes Run.new is safe to encode and ship to the guest.

Run is the host→guest entrypoint dispatch envelope (the #run request shape), the symmetric counterpart to the guest→host Request envelope. #encode takes the Sandbox's Catalog::Handles and routes any non-wire-representable args / kwargs leaf through it as a Kobako::Handle — the symmetric counterpart of the guest→host wrap path in the dispatcher. A Kobako::Handle that arrives already constructed in the caller's args / kwargs is rejected at construction: legitimate Handles only enter Host App code through error fields, so a Handle reaching the call site is by definition smuggled in. The #encode output is the "Run envelope" that ships through the __kobako_run command buffer.

Built on the class X < Data.define(...) subclass form (the Steep-friendly shape — see lib/kobako/outcome/panic.rb).

Constant Summary collapse

NAME_PATTERN =

Ruby constant-name pattern enforced on the entrypoint Symbol. Parallel to Kobako::Catalog::Snippets::NAME_PATTERN; the two constants name the same regex but cover distinct surfaces (snippet identity vs. entrypoint resolution) so a future divergence stays local.

Returns:

  • (Regexp)
/\A[A-Z]\w*\z/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(entrypoint:, args: [], kwargs: {}) ⇒ Run

Returns a new instance of Run.

Parameters:

  • entrypoint: (Symbol, String)
  • args: (Array[untyped]) (defaults to: [])
  • kwargs: (Hash[untyped, untyped]) (defaults to: {})


44
45
46
47
48
49
# File 'lib/kobako/transport/run.rb', line 44

def initialize(entrypoint:, args: [], kwargs: {})
  entrypoint = normalize_entrypoint(entrypoint)
  validate_args!(args)
  validate_kwargs!(kwargs)
  super
end

Instance Attribute Details

#argsArray[untyped] (readonly)

Returns the value of attribute args.

Returns:

  • (Array[untyped])


7
8
9
# File 'sig/kobako/transport/run.rbs', line 7

def args
  @args
end

#entrypointSymbol (readonly)

Returns the value of attribute entrypoint.

Returns:

  • (Symbol)


6
7
8
# File 'sig/kobako/transport/run.rbs', line 6

def entrypoint
  @entrypoint
end

#kwargsHash[Symbol, untyped] (readonly)

Returns the value of attribute kwargs.

Returns:

  • (Hash[Symbol, untyped])


8
9
10
# File 'sig/kobako/transport/run.rbs', line 8

def kwargs
  @kwargs
end

Class Method Details

.newRun

Parameters:

  • entrypoint: (Symbol, String)
  • args: (Array[untyped])
  • kwargs: (Hash[untyped, untyped])

Returns:



10
# File 'sig/kobako/transport/run.rbs', line 10

def self.new: (entrypoint: Symbol | String, ?args: Array[untyped], ?kwargs: Hash[untyped, untyped]) -> Run

Instance Method Details

#encode(handler) ⇒ String

Encode this Run to the msgpack bytes the guest's __kobako_run entry point consumes as its command-buffer payload (docs/wire-codec.md Invocation channels). Walks args / kwargs through Codec::HandleWalk.deep_wrap so any non-wire-representable leaf is allocated into handler and replaced with a Kobako::Handle; the handler argument is the Sandbox's table, sharing the same allocator the guest→host return path uses.

Layout: msgpack map with string keys "entrypoint" (Symbol via ext 0x00), "args" (Array), "kwargs" (Map with Symbol keys); any wrapped leaf rides as ext 0x01 in its original position (docs/wire-codec.md § ext 0x01 position rules).

Parameters:

Returns:

  • (String)


64
65
66
67
68
69
70
# File 'lib/kobako/transport/run.rb', line 64

def encode(handler)
  Codec::Encoder.encode(
    "entrypoint" => entrypoint,
    "args" => Codec::HandleWalk.deep_wrap(args, handler),
    "kwargs" => Codec::HandleWalk.deep_wrap(kwargs, handler)
  )
end

#forged_handle_message(slot) ⇒ String

Single source of truth for the forged-Handle reject message so the args and kwargs branches stay phrased identically. Message stays in caller vocabulary: it names the affected slot and the reason without leaking internal SPEC identifiers or self-referential architecture terms — the error is raised BY kobako, so saying "allocated by the Host Gem" reads as third-person about self.

Parameters:

  • slot (String)

Returns:

  • (String)


126
127
128
129
# File 'lib/kobako/transport/run.rb', line 126

def forged_handle_message(slot)
  "#{slot} must not contain a Kobako::Handle — " \
    "Handles are created internally by the Sandbox and cannot be passed in"
end

#normalize_entrypoint(target) ⇒ Symbol

The target must be a Symbol or String (TypeError, not ArgumentError — the wrong-type case is a Host App programming error before the run reaches the guest). After .to_s the value must match NAME_PATTERN (ArgumentError), rejecting +::+-segmented names and any non-constant form.

Parameters:

  • target (Symbol, String)

Returns:

  • (Symbol)


79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/kobako/transport/run.rb', line 79

def normalize_entrypoint(target)
  unless target.is_a?(Symbol) || target.is_a?(String)
    raise TypeError, "entrypoint must be a Symbol or String, got #{target.class}"
  end

  target_str = target.to_s
  unless NAME_PATTERN.match?(target_str)
    raise ArgumentError,
          "entrypoint must match #{NAME_PATTERN.inspect} (got #{target.inspect})"
  end

  target_str.to_sym
end

#validate_args!(args) ⇒ void

This method returns an undefined value.

args must not contain a Kobako::Handle. The Handle allocator lives inside the Host Gem; legitimate paths surface Handle objects only through raised error fields, so a Handle reaching args is a forged or smuggled token. Non-wire- representable arguments that are not Handles are handled by auto-wrap inside #encode — the reject path is reserved for Handle objects specifically.

Parameters:

  • args (Array[untyped])

Raises:

  • (ArgumentError)


100
101
102
103
# File 'lib/kobako/transport/run.rb', line 100

def validate_args!(args)
  raise ArgumentError, "arguments must be an Array" unless args.is_a?(Array)
  raise ArgumentError, forged_handle_message("arguments") if args.any?(Kobako::Handle)
end

#validate_kwargs!(kwargs) ⇒ void

This method returns an undefined value.

Reject a non-Symbol kwargs key, and a Kobako::Handle arriving as a kwargs value (same forged-token principle as the args branch). Both checks live here so the Host App sees the host-side error message before any encode / decode boundary.

Parameters:

  • kwargs (Hash[untyped, untyped])

Raises:

  • (ArgumentError)


109
110
111
112
113
114
115
116
117
118
# File 'lib/kobako/transport/run.rb', line 109

def validate_kwargs!(kwargs)
  raise ArgumentError, "keyword arguments must be a Hash" unless kwargs.is_a?(Hash)

  bad_keys = kwargs.each_key.grep_v(Symbol)
  unless bad_keys.empty?
    raise ArgumentError,
          "keyword argument keys must be Symbols (got #{bad_keys.inspect})"
  end
  raise ArgumentError, forged_handle_message("keyword argument values") if kwargs.each_value.any?(Kobako::Handle)
end