kobako 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +7 -0
  4. data/Cargo.lock +1 -1
  5. data/README.md +14 -7
  6. data/data/kobako.wasm +0 -0
  7. data/ext/kobako/Cargo.toml +2 -2
  8. data/ext/kobako/src/runtime/ambient.rs +1 -1
  9. data/ext/kobako/src/runtime/cache.rs +3 -3
  10. data/ext/kobako/src/runtime/capture.rs +1 -1
  11. data/ext/kobako/src/runtime/config.rs +3 -4
  12. data/ext/kobako/src/runtime/dispatch.rs +6 -6
  13. data/ext/kobako/src/runtime/exports.rs +1 -1
  14. data/ext/kobako/src/runtime/instance_pre.rs +1 -1
  15. data/ext/kobako/src/runtime/invocation.rs +27 -27
  16. data/ext/kobako/src/runtime/trap.rs +5 -5
  17. data/ext/kobako/src/runtime.rs +44 -45
  18. data/ext/kobako/src/snapshot.rs +2 -2
  19. data/lib/kobako/capture.rb +5 -7
  20. data/lib/kobako/catalog/handles.rb +24 -31
  21. data/lib/kobako/catalog/namespaces.rb +19 -27
  22. data/lib/kobako/catalog/snippets.rb +10 -16
  23. data/lib/kobako/codec/utils.rb +6 -9
  24. data/lib/kobako/errors.rb +33 -39
  25. data/lib/kobako/handle.rb +2 -3
  26. data/lib/kobako/namespace.rb +8 -11
  27. data/lib/kobako/outcome.rb +12 -14
  28. data/lib/kobako/pool.rb +18 -24
  29. data/lib/kobako/sandbox.rb +61 -83
  30. data/lib/kobako/sandbox_options.rb +5 -9
  31. data/lib/kobako/snapshot.rb +2 -4
  32. data/lib/kobako/snippet/binary.rb +1 -3
  33. data/lib/kobako/snippet/source.rb +1 -2
  34. data/lib/kobako/snippet.rb +1 -2
  35. data/lib/kobako/transport/dispatcher.rb +39 -38
  36. data/lib/kobako/transport/request.rb +1 -1
  37. data/lib/kobako/transport/run.rb +23 -28
  38. data/lib/kobako/transport/yielder.rb +11 -17
  39. data/lib/kobako/transport.rb +2 -3
  40. data/lib/kobako/usage.rb +10 -13
  41. data/lib/kobako/version.rb +1 -1
  42. data/sig/kobako/transport/dispatcher.rbs +2 -0
  43. metadata +1 -1
@@ -19,8 +19,7 @@ module Kobako
19
19
  # - Representability predicate ({representable?}) and the symmetric
20
20
  # host→guest +#run+ argument walk ({deep_wrap}) used by
21
21
  # +Kobako::Transport::Run#encode+ to route non-representable leaves
22
- # through the Sandbox's +Kobako::Catalog::Handles+
23
- # ({docs/behavior.md B-34}[link:../../../docs/behavior.md]).
22
+ # through the Sandbox's +Kobako::Catalog::Handles+.
24
23
  #
25
24
  # All helpers are pure — they only inspect inputs, never mutate
26
25
  # them — except {deep_wrap}, whose only side effect is allocating
@@ -84,8 +83,7 @@ module Kobako
84
83
 
85
84
  # Deep-walk Array / Hash containers in +value+ and replace every
86
85
  # leaf that fails {representable?} with a +Kobako::Handle+
87
- # allocated from +handler+
88
- # ({docs/behavior.md B-34}[link:../../../docs/behavior.md]). The
86
+ # allocated from +handler+. The
89
87
  # walk only descends through representable container shapes
90
88
  # (Array, Hash) one structural level at a time; a non-representable
91
89
  # leaf is wrapped as-is without inspecting its internal structure.
@@ -116,8 +114,7 @@ module Kobako
116
114
 
117
115
  # Deep-walk Array / Hash containers in +value+ and replace every
118
116
  # +Kobako::Handle+ leaf with the host-side object +handler+ resolves
119
- # it to ({docs/behavior.md B-37}[link:../../../docs/behavior.md]).
120
- # The symmetric inverse of {deep_wrap}: that walk allocates objects
117
+ # it to. The symmetric inverse of {deep_wrap}: that walk allocates objects
121
118
  # into Handles on the host→guest argument path; this walk resolves
122
119
  # Handles back to their objects on every guest→host value path — the
123
120
  # +#eval+ / +#run+ result and the yield-block result alike. The walk
@@ -126,11 +123,11 @@ module Kobako
126
123
  # unchanged.
127
124
  #
128
125
  # +value+ is a decoded Ruby value (a Handle here is a wire-decoded
129
- # +Kobako::Handle+, never a guest-forged one — B-20); +handler+ must
126
+ # +Kobako::Handle+, never a guest-forged one); +handler+ must
130
127
  # respond to +#fetch(id) -> object+ (a host-side
131
128
  # +Kobako::Catalog::Handles+). +handler.fetch+ raises
132
- # +Kobako::SandboxError+ for an id with no live binding, which is the
133
- # corrupted-runtime fallback B-37 specifies.
129
+ # +Kobako::SandboxError+ for an id with no live binding, the
130
+ # corrupted-runtime fallback.
134
131
  def deep_restore(value, handler)
135
132
  case value
136
133
  when ::Array then value.map { |element| Utils.deep_restore(element, handler) }
data/lib/kobako/errors.rb CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  # Top-level Kobako namespace.
4
4
  module Kobako
5
- # Error taxonomy (docs/behavior.md § Error Scenarios).
5
+ # Error taxonomy.
6
6
  #
7
7
  # Every `Kobako::Sandbox` invocation (`#eval` or `#run`) either returns a value or raises
8
8
  # exactly one of three invocation-outcome classes. Attribution is decided after the
9
- # guest binary returns control to the host (docs/behavior.md
10
- # "Step 1 — Wasm trap" then "Step 2 — Outcome envelope tag").
9
+ # guest binary returns control to the host: first the Wasm-trap layer, then
10
+ # the outcome-envelope tag.
11
11
  #
12
12
  # Three invocation-outcome branches:
13
13
  #
@@ -25,19 +25,17 @@ module Kobako
25
25
  #
26
26
  # * {SetupError} — construction layer. Raised by `Kobako::Sandbox.new`
27
27
  # when the wasm runtime cannot be built from the
28
- # configured +wasm_path+ before any invocation runs
29
- # ({docs/behavior.md E-40 / E-41}[link:../../docs/behavior.md]).
28
+ # configured +wasm_path+ before any invocation runs.
30
29
  # Not an invocation outcome, so it never passes
31
30
  # through the two-step attribution decision.
32
31
  # * {PoolTimeoutError} — pool checkout layer. Raised by `Kobako::Pool#with`
33
- # when the checkout wait exceeds +checkout_timeout+
34
- # ({docs/behavior.md E-46}[link:../../docs/behavior.md]).
32
+ # when the checkout wait exceeds +checkout_timeout+.
35
33
  #
36
- # Subclasses pinned by docs/behavior.md Error Classes:
34
+ # Named subclasses:
37
35
  #
38
36
  # * {ModuleNotBuiltError} < {SetupError} — Guest Binary artifact absent
39
- # at +wasm_path+ (E-40).
40
- # * {HandlerExhaustedError} < {SandboxError} — Handle id cap hit (B-21).
37
+ # at +wasm_path+.
38
+ # * {HandlerExhaustedError} < {SandboxError} — Handle id cap hit.
41
39
 
42
40
  # Base for all kobako-raised errors so callers that want to ignore the
43
41
  # taxonomy can rescue a single class.
@@ -46,13 +44,13 @@ module Kobako
46
44
  # Wasm engine layer. Raised when the Wasm execution engine crashed
47
45
  # (trap, OOM, unreachable) or when the wire layer detected a structural
48
46
  # violation that signals a corrupted guest execution environment
49
- # (zero-length OUTCOME_BUFFER, unknown outcome tag — SPEC E-02 / E-03).
47
+ # (zero-length OUTCOME_BUFFER, unknown outcome tag).
50
48
  #
51
- # Two named subclasses cover the configured per-invocation caps from B-01:
49
+ # Two named subclasses cover the configured per-invocation caps:
52
50
  #
53
- # * {TimeoutError} — wall-clock +timeout+ exceeded (E-19).
51
+ # * {TimeoutError} — wall-clock +timeout+ exceeded.
54
52
  # * {MemoryLimitError} — guest +memory.grow+ would exceed
55
- # +memory_limit+ (E-20).
53
+ # +memory_limit+.
56
54
  #
57
55
  # Host Apps that only care about "guest is unrecoverable, discard the
58
56
  # Sandbox" can rescue +TrapError+ and ignore the subclass; Host Apps that
@@ -60,24 +58,23 @@ module Kobako
60
58
  # first.
61
59
  class TrapError < Error; end
62
60
 
63
- # Wall-clock timeout cap exhausted. {docs/behavior.md E-19}[link:../../docs/behavior.md]:
64
- # the absolute deadline +entry_time + timeout+ passed and the next guest
65
- # wasm safepoint trapped. The Sandbox is unrecoverable after this point;
66
- # discard and recreate before another execution.
61
+ # Wall-clock timeout cap exhausted: the absolute deadline
62
+ # +entry_time + timeout+ passed and the next guest wasm safepoint
63
+ # trapped. The Sandbox is unrecoverable after this point; discard and
64
+ # recreate before another execution.
67
65
  class TimeoutError < TrapError; end
68
66
 
69
- # Linear-memory cap exhausted. {docs/behavior.md E-20}[link:../../docs/behavior.md]:
70
- # a guest +memory.grow+ would have pushed linear memory past the
71
- # configured +memory_limit+. The Sandbox is unrecoverable after this
72
- # point; discard and recreate before another execution.
67
+ # Linear-memory cap exhausted: a guest +memory.grow+ would have pushed
68
+ # linear memory past the configured +memory_limit+. The Sandbox is
69
+ # unrecoverable after this point; discard and recreate before another
70
+ # execution.
73
71
  class MemoryLimitError < TrapError; end
74
72
 
75
73
  # Construction-layer error raised by +Kobako::Sandbox.new+ /
76
74
  # +Kobako::Runtime.from_path+ when the wasm runtime cannot be built
77
75
  # from the configured +wasm_path+ before any invocation runs —
78
76
  # an unreadable artifact, bytes that are not a valid Wasm module, or
79
- # engine / linker / instantiation setup failure
80
- # ({docs/behavior.md E-41}[link:../../docs/behavior.md]). Construction
77
+ # engine / linker / instantiation setup failure. Construction
81
78
  # is not an invocation, so +SetupError+ sits beside the invocation
82
79
  # taxonomy under +Kobako::Error+ rather than under +TrapError+: no
83
80
  # Sandbox is produced, so the +TrapError+ "discard and recreate"
@@ -86,8 +83,7 @@ module Kobako
86
83
 
87
84
  # The named +SetupError+ subclass for the common, actionable case:
88
85
  # the Guest Binary artifact is absent at +wasm_path+ — the pre-build
89
- # state on a fresh clone before +bundle exec rake compile+
90
- # ({docs/behavior.md E-40}[link:../../docs/behavior.md]). Host Apps
86
+ # state on a fresh clone before +bundle exec rake compile+. Host Apps
91
87
  # that only need "the Sandbox could not be set up" rescue +SetupError+;
92
88
  # those wanting to special-case the unbuilt-artifact state rescue
93
89
  # +ModuleNotBuiltError+ first.
@@ -123,19 +119,18 @@ module Kobako
123
119
  end
124
120
  end
125
121
 
126
- # docs/behavior.md Error Classes: HandlerExhaustedError is the canonical
127
- # SandboxError subclass for the id-cap-hit path (B-21). Raised when the
128
- # per-invocation Handle ID counter in Catalog::Handles reaches
129
- # +0x7fff_ffff+ (2³¹ − 1) and further allocation would exceed the cap.
122
+ # HandlerExhaustedError is the canonical SandboxError subclass for the
123
+ # id-cap-hit path. Raised when the per-invocation Handle ID counter in
124
+ # Catalog::Handles reaches +0x7fff_ffff+ (2³¹ 1) and further
125
+ # allocation would exceed the cap.
130
126
  class HandlerExhaustedError < SandboxError; end
131
127
 
132
- # docs/behavior.md Error Classes: BytecodeError is the SandboxError
133
- # subclass raised when a `#preload(binary:)` snippet fails structural
134
- # validation during the first invocation's snippet replay against a
135
- # fresh `mrb_state` (E-37 RITE version mismatch, E-38 corrupt body).
136
- # Bytecode that loads cleanly and then raises at top level is E-36
137
- # and surfaces as plain `SandboxError` with the natural mruby class
138
- # preserved. Inherits from SandboxError so a single
128
+ # BytecodeError is the SandboxError subclass raised when a
129
+ # `#preload(binary:)` snippet fails structural validation during the
130
+ # first invocation's snippet replay against a fresh `mrb_state` (RITE
131
+ # version mismatch or corrupt body). Bytecode that loads cleanly and
132
+ # then raises at top level surfaces as plain `SandboxError` with the
133
+ # natural mruby class preserved. Inherits from SandboxError so a single
139
134
  # `rescue Kobako::SandboxError` covers both source and bytecode
140
135
  # snippet failures while callers wanting bytecode-specific handling
141
136
  # can `rescue Kobako::BytecodeError` directly.
@@ -143,8 +138,7 @@ module Kobako
143
138
 
144
139
  # Pool checkout layer. Raised by +Kobako::Pool#with+ when the checkout
145
140
  # wait exceeded the configured +checkout_timeout+ while every slot was
146
- # held ({docs/behavior.md E-46}[link:../../docs/behavior.md]). No
147
- # Sandbox state is touched — retrying succeeds as soon as a holder
141
+ # held. No Sandbox state is touched — retrying succeeds as soon as a holder
148
142
  # returns its Sandbox.
149
143
  class PoolTimeoutError < Error; end
150
144
  end
data/lib/kobako/handle.rb CHANGED
@@ -3,9 +3,8 @@
3
3
  module Kobako
4
4
  # Wire-level value object for an ext-0x01 Capability Handle, used in both
5
5
  # directions across the Sandbox boundary: as a Service method's return
6
- # value (guest→host return path; {docs/behavior.md B-14}[link:../../docs/behavior.md])
7
- # and as a +#run+ argument auto-wrapped by the host
8
- # ({docs/behavior.md B-34}[link:../../docs/behavior.md]).
6
+ # value (guest→host return path) and as a +#run+ argument auto-wrapped
7
+ # by the host.
9
8
  #
10
9
  # SPEC pins the binary layout to fixext 4 with a 4-byte big-endian u32
11
10
  # payload ({docs/wire-codec.md}[link:../../docs/wire-codec.md]
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kobako
4
- # A named grouping of Members for one Sandbox
5
- # ({docs/behavior.md B-07..B-11}[link:../../docs/behavior.md]).
4
+ # A named grouping of Members for one Sandbox.
6
5
  # Returned by +Sandbox#define+. Each instance owns a flat name→object
7
6
  # table of Members; member binding is validated against {NAME_PATTERN}.
8
7
  class Namespace
9
- # Ruby constant-name pattern shared by Namespace and Member names
10
- # ({docs/behavior.md B-07/B-08 Notes}[link:../../docs/behavior.md]).
8
+ # Ruby constant-name pattern shared by Namespace and Member names.
11
9
  NAME_PATTERN = /\A[A-Z]\w*\z/
12
10
 
13
11
  attr_reader :name
@@ -26,9 +24,8 @@ module Kobako
26
24
  # object that responds to the methods guest code will invoke. Returns
27
25
  # +self+ for chaining. Raises +ArgumentError+ when +member+ does not
28
26
  # match the constant pattern, when a Member of the same name is
29
- # already bound ({docs/behavior.md B-11}[link:../../docs/behavior.md]),
30
- # or when the owning Sandbox's first invocation has sealed Service
31
- # registration ({docs/behavior.md E-45}[link:../../docs/behavior.md]).
27
+ # already bound, or when the owning Sandbox's first invocation has
28
+ # sealed Service registration.
32
29
  def bind(member, object)
33
30
  raise ArgumentError, "cannot bind after first Sandbox invocation" if @sealed
34
31
 
@@ -39,10 +36,10 @@ module Kobako
39
36
  self
40
37
  end
41
38
 
42
- # Mark this Namespace as sealed ({docs/behavior.md B-33}[link:../../docs/behavior.md]).
43
- # Called by +Kobako::Catalog::Namespaces#seal!+ on the owning
44
- # Sandbox's first invocation; afterwards {#bind} raises
45
- # +ArgumentError+ (E-45). Idempotent; returns +self+.
39
+ # Mark this Namespace as sealed. Called by
40
+ # +Kobako::Catalog::Namespaces#seal!+ on the owning Sandbox's first
41
+ # invocation; afterwards {#bind} raises +ArgumentError+. Idempotent;
42
+ # returns +self+.
46
43
  def seal!
47
44
  @sealed = true
48
45
  self
@@ -7,8 +7,7 @@ module Kobako
7
7
  # Host-facing boundary for the OUTCOME_BUFFER produced by
8
8
  # +__kobako_eval+. Takes raw outcome bytes — a one-byte tag followed by
9
9
  # the msgpack-encoded body — and maps them to either the unwrapped
10
- # mruby return value or a raised three-layer
11
- # ({docs/behavior.md Error Scenarios}[link:../../docs/behavior.md]) exception.
10
+ # mruby return value or a raised three-layer exception.
12
11
  #
13
12
  # Self-contained: this module owns the wire framing (tag bytes,
14
13
  # body decoding), and the +Panic+ wire record lives at
@@ -17,11 +16,11 @@ module Kobako
17
16
  # nothing in +Transport+ participates.
18
17
  #
19
18
  # * tag 0x01, decode OK → return decoded value
20
- # * tag 0x01, decode fails → SandboxError (E-09)
21
- # * tag 0x02, origin="service" → ServiceError (E-13)
22
- # * tag 0x02, origin="sandbox"/missing → SandboxError (E-04..E-07)
23
- # * tag 0x02, decode fails → SandboxError (E-08)
24
- # * unknown tag → TrapError (E-03)
19
+ # * tag 0x01, decode fails → SandboxError
20
+ # * tag 0x02, origin="service" → ServiceError
21
+ # * tag 0x02, origin="sandbox"/missing → SandboxError
22
+ # * tag 0x02, decode fails → SandboxError
23
+ # * unknown tag → TrapError
25
24
  module Outcome
26
25
  # First byte of the OUTCOME_BUFFER for the success branch — body is
27
26
  # the bare msgpack encoding of the returned value
@@ -71,7 +70,7 @@ module Kobako
71
70
  [tag, body]
72
71
  end
73
72
 
74
- # Decode failure on the success tag is a SandboxError (E-09): the
73
+ # Decode failure on the success tag is a SandboxError: the
75
74
  # framing was fine, but the carried value is unrepresentable. The
76
75
  # specific codec fault is stashed in +details+ rather
77
76
  # than spliced into the message — callers cannot act on the inner
@@ -86,7 +85,7 @@ module Kobako
86
85
  )
87
86
  end
88
87
 
89
- # Decode failure on the panic tag is a SandboxError (E-08). Either
88
+ # Decode failure on the panic tag is a SandboxError. Either
90
89
  # path raises — on success the decoded Panic is mapped to its three-
91
90
  # layer exception via +build_panic_error+ and raised; on wire-decode
92
91
  # failure the rescue path raises the wire-violation +SandboxError+.
@@ -130,13 +129,12 @@ module Kobako
130
129
  )
131
130
  end
132
131
 
133
- # {docs/behavior.md Error Scenarios}[link:../../docs/behavior.md]: map
134
- # the panic +class+ field to the matching Ruby exception subclass so
135
- # callers can rescue specific failure paths. +origin="service"+ →
132
+ # Map the panic +class+ field to the matching Ruby exception subclass
133
+ # so callers can rescue specific failure paths. +origin="service"+
136
134
  # +ServiceError+; +origin="sandbox"+ plus
137
135
  # +class="Kobako::BytecodeError"+ selects the +BytecodeError+
138
- # subclass (E-37 / E-38). Everything else falls back to the base
139
- # class for the origin.
136
+ # subclass. Everything else falls back to the base class for the
137
+ # origin.
140
138
  def panic_target_class(panic)
141
139
  case panic.origin
142
140
  when Panic::ORIGIN_SERVICE
data/lib/kobako/pool.rb CHANGED
@@ -5,32 +5,28 @@ require_relative "sandbox"
5
5
 
6
6
  module Kobako
7
7
  # Kobako::Pool — a bounded set of warm, identically set-up Sandboxes
8
- # handed out one exclusive holder at a time
9
- # ({docs/behavior.md B-46..B-48}[link:../../docs/behavior.md]).
8
+ # handed out one exclusive holder at a time.
10
9
  #
11
10
  # Construction forwards every +Kobako::Sandbox.new+ keyword verbatim
12
11
  # and holds the optional block as the per-Sandbox setup hook; a
13
12
  # checkout prefers an idle Sandbox and constructs a new one only when
14
- # none is idle and fewer than +slots+ exist (B-46). +#with+ blocks up
15
- # to +checkout_timeout+ seconds when every slot is held (E-46), applies
16
- # the +TrapError+ discard-and-recreate contract at checkin (B-47), and
13
+ # none is idle and fewer than +slots+ exist. +#with+ blocks up
14
+ # to +checkout_timeout+ seconds when every slot is held, applies
15
+ # the +TrapError+ discard-and-recreate contract at checkin, and
17
16
  # the Pool releases everything with its own reachability — there is no
18
- # teardown verb (B-48).
17
+ # teardown verb.
19
18
  class Pool
20
- # The +#with+ wait bound applied when +checkout_timeout+ is not given
21
- # ({docs/behavior.md B-46}[link:../../docs/behavior.md]).
19
+ # The +#with+ wait bound applied when +checkout_timeout+ is not given.
22
20
  DEFAULT_CHECKOUT_TIMEOUT_SECONDS = 5.0
23
21
 
24
- # Build a Pool of up to +slots+ Sandboxes
25
- # ({docs/behavior.md B-46}[link:../../docs/behavior.md]). +slots+ is
22
+ # Build a Pool of up to +slots+ Sandboxes. +slots+ is
26
23
  # a positive Integer; +checkout_timeout+ bounds the +#with+ wait in
27
24
  # seconds (+nil+ waits indefinitely); every other keyword is
28
25
  # forwarded verbatim to +Kobako::Sandbox.new+. The optional block
29
26
  # runs exactly once per constructed Sandbox — it is the setup window
30
27
  # for +#define+ / +#preload+ before that Sandbox's first checkout.
31
28
  # No Sandbox is constructed here. Raises +ArgumentError+ for an
32
- # invalid +slots+ / +checkout_timeout+
33
- # ({docs/behavior.md E-47}[link:../../docs/behavior.md]).
29
+ # invalid +slots+ / +checkout_timeout+.
34
30
  def initialize(slots:, checkout_timeout: DEFAULT_CHECKOUT_TIMEOUT_SECONDS, **sandbox_options, &setup)
35
31
  validate_slots!(slots)
36
32
  @slots = slots
@@ -44,11 +40,9 @@ module Kobako
44
40
  end
45
41
 
46
42
  # Yield one exclusively-held Sandbox to the block and return the
47
- # block's value ({docs/behavior.md B-47}[link:../../docs/behavior.md]).
48
- # Blocks while every slot is held; raises +Kobako::PoolTimeoutError+
49
- # once the wait exceeds +checkout_timeout+
50
- # ({docs/behavior.md E-46}[link:../../docs/behavior.md]). The Sandbox
51
- # returns to the pool at block exit — unless the block raised
43
+ # block's value. Blocks while every slot is held; raises
44
+ # +Kobako::PoolTimeoutError+ once the wait exceeds +checkout_timeout+.
45
+ # The Sandbox returns to the pool at block exit — unless the block raised
52
46
  # +Kobako::TrapError+, in which case the unrecoverable Sandbox is
53
47
  # discarded and its slot refills by a fresh construction on next
54
48
  # demand.
@@ -68,12 +62,12 @@ module Kobako
68
62
  private
69
63
 
70
64
  # Acquire a Sandbox and hand it over in pre-invocation state — empty
71
- # output buffers and truncation predicates false (B-47).
65
+ # output buffers and truncation predicates false.
72
66
  def checkout
73
67
  acquire.tap(&:reset_invocation_state!)
74
68
  end
75
69
 
76
- # The idle-first claim loop (B-46): an idle Sandbox wins, unclaimed
70
+ # The idle-first claim loop: an idle Sandbox wins, unclaimed
77
71
  # capacity constructs, and a full pool waits for a checkin.
78
72
  def acquire
79
73
  timeout = @checkout_timeout
@@ -105,7 +99,7 @@ module Kobako
105
99
  end
106
100
 
107
101
  # Wait for a checkin or freed capacity; raises
108
- # +Kobako::PoolTimeoutError+ once +deadline+ has passed (E-46). Must
102
+ # +Kobako::PoolTimeoutError+ once +deadline+ has passed. Must
109
103
  # run while holding +@mutex+.
110
104
  def await_slot!(deadline)
111
105
  remaining = deadline && (deadline - monotonic_now)
@@ -119,7 +113,7 @@ module Kobako
119
113
 
120
114
  # Construct and set up one pooled Sandbox against the capacity
121
115
  # reserved by +claim_or_wait+. Construction and setup-block errors
122
- # propagate to the checkout caller unchanged (B-46); the reserved
116
+ # propagate to the checkout caller unchanged; the reserved
123
117
  # capacity is released so a later checkout can retry.
124
118
  def construct_slot
125
119
  done = false
@@ -154,7 +148,7 @@ module Kobako
154
148
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
155
149
  end
156
150
 
157
- # E-47 pre-flight for +slots+ — no coercion, a positive Integer is
151
+ # Pre-flight for +slots+ — no coercion, a positive Integer is
158
152
  # the only accepted shape.
159
153
  def validate_slots!(slots)
160
154
  return if slots.is_a?(Integer) && slots.positive?
@@ -163,8 +157,8 @@ module Kobako
163
157
  end
164
158
 
165
159
  # Coerce +checkout_timeout+ into the Float seconds the wait loop
166
- # consumes, or +nil+ to wait indefinitely — the E-39 normalisation
167
- # idiom applied to E-47.
160
+ # consumes, or +nil+ to wait indefinitely — the same normalisation
161
+ # idiom +SandboxOptions+ applies to +timeout+.
168
162
  def normalize_checkout_timeout(checkout_timeout)
169
163
  return nil if checkout_timeout.nil?
170
164
  unless checkout_timeout.is_a?(Numeric)