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 ship to the guest.

#payload takes the invocation'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. Runtime#run frames that payload with the entrypoint into the Run envelope the __kobako_run command buffer carries.

Built on the class X < Data.define(...) subclass form (the Steep-friendly shape — see .rubocop.yml for the rationale).

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: {})


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

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

Instance Attribute Details

#argsArray[Kobako::Codec::wire_value] (readonly)

Returns the value of attribute args.

Returns:

  • (Array[Kobako::Codec::wire_value])


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, Kobako::Codec::wire_value] (readonly)

Returns the value of attribute kwargs.

Returns:

  • (Hash[Symbol, Kobako::Codec::wire_value])


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

#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)


118
119
120
121
# File 'lib/kobako/transport/run.rb', line 118

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)


71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/kobako/transport/run.rb', line 71

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

#payload(handler) ⇒ String

Encode this Run's arguments as the codec payload the Run envelope carries — Runtime#run frames it with the entrypoint. 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 invocation's table, sharing the same allocator the guest→host return path uses. A wrapped leaf rides as ext 0x01 in its original position (docs/wire/payload-msgpack.md § ext 0x01).

Parameters:

Returns:

  • (String)


57
58
59
60
61
62
# File 'lib/kobako/transport/run.rb', line 57

def payload(handler)
  Payload::Arguments.new(
    args: Codec::HandleWalk.deep_wrap(args, handler),
    kwargs: Codec::HandleWalk.deep_wrap(kwargs, handler)
  ).encode
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 #payload — the reject path is reserved for Handle objects specifically.

Parameters:

  • args (Array[untyped])

Raises:

  • (ArgumentError)


92
93
94
95
# File 'lib/kobako/transport/run.rb', line 92

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)


101
102
103
104
105
106
107
108
109
110
# File 'lib/kobako/transport/run.rb', line 101

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