Class: Coatepec::Spec::GuardedForkStrategy
- Inherits:
-
ForkStrategy
- Object
- ProcessStrategy
- ForkStrategy
- Coatepec::Spec::GuardedForkStrategy
- Defined in:
- lib/coatepec/spec/guarded_fork_strategy.rb
Overview
Opt-in macOS fork strategy: attempts Process.fork like ForkStrategy (reusing the warm worker's boot), but only after two cheap guard checks pass, and transparently falls back to a fresh SpawnStrategy run -- for this call only -- when a guard fails or the forked child crashes. See docs/superpowers/specs/2026-07-31-macos-guarded-fork-design.md.
Constant Summary collapse
- BUILTIN_UNSAFE_GEMS =
Intentionally empty at ship time: the one documented crash this guards against didn't name a specific culprit gem, just "something ObjC-initializing on another thread." This is an extension point for real incidents (refine via a project's own .coatepec.yml macos_fork_unsafe_gems), not a researched-and-complete list.
[].freeze
- THREAD_COUNT_TOLERANCE =
Live thread count is allowed to exceed the post-boot baseline by this much before the guard treats it as "something is mid-init."
1- CRASH_SIGNAL_NAMES =
%w[ABRT SEGV BUS].freeze
- MIN_RETRY_TIMEOUT_SECONDS =
A crash retry shares one timeout budget with the fork attempt that preceded it: WorkerManager only gives the whole dispatch timeout_seconds + 10 before Client#read_response raises DisconnectedError and the warm worker is restarted from scratch -- exactly the boot this feature exists to preserve. The floor keeps a nearly-exhausted budget from turning the retry into a guaranteed timeout kill; a few seconds is enough for a fast spec to still land.
5- MAX_CRASH_STDERR_BYTES =
The crashed child's stderr carries the macOS crash report -- the only evidence that could ever populate BUILTIN_UNSAFE_GEMS from a real incident. Kept far below Result::MAX_OUTPUT_BYTES because it rides along with a whole second result inside MCP::Response's 1 MiB cap.
4 * 1024
Instance Method Summary collapse
-
#initialize(project_root, project: nil, rails_runtime: nil) ⇒ GuardedForkStrategy
constructor
A new instance of GuardedForkStrategy.
- #run(args, timeout_seconds) ⇒ Object
Constructor Details
#initialize(project_root, project: nil, rails_runtime: nil) ⇒ GuardedForkStrategy
Returns a new instance of GuardedForkStrategy.
39 40 41 42 |
# File 'lib/coatepec/spec/guarded_fork_strategy.rb', line 39 def initialize(project_root, project: nil, rails_runtime: nil) super @spawn_strategy = SpawnStrategy.new(project_root) end |
Instance Method Details
#run(args, timeout_seconds) ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/coatepec/spec/guarded_fork_strategy.rb', line 44 def run(args, timeout_seconds) return fallback_result(args, timeout_seconds, "spawn_fallback") unless guard_passes? started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC) begin result = super rescue SystemCallError # Process.fork itself failed (Errno::EAGAIN/ENOMEM under # process-table pressure), so no child was ever produced -- from the # caller's side that is indistinguishable from a failed guard, hence # the same mode. Opting into macos_fork must never surface an error # that plain SpawnStrategy wouldn't have. return fallback_result(args, timeout_seconds, "spawn_fallback") end return result.merge(execution_mode: "fork") unless crashed?(result) retry_after_crash(args, timeout_seconds, started_at, result) end |