rubik 0.1.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.
- checksums.yaml +7 -0
- data/ATTRIBUTION.md +139 -0
- data/CHANGELOG.md +236 -0
- data/LICENSE +21 -0
- data/README.md +269 -0
- data/bin/rubik +82 -0
- data/lib/rubik/adapter/base.rb +195 -0
- data/lib/rubik/adapter/c.rb +36 -0
- data/lib/rubik/adapter/crystal.rb +78 -0
- data/lib/rubik/adapter/generic.rb +86 -0
- data/lib/rubik/adapter/go.rb +36 -0
- data/lib/rubik/adapter/node.rb +146 -0
- data/lib/rubik/adapter/process.rb +78 -0
- data/lib/rubik/adapter/python.rb +30 -0
- data/lib/rubik/adapter/registry.rb +81 -0
- data/lib/rubik/adapter/ruby.rb +30 -0
- data/lib/rubik/adapter/rust.rb +36 -0
- data/lib/rubik/adapter/zig.rb +36 -0
- data/lib/rubik/adapter.rb +18 -0
- data/lib/rubik/ast/body_loader.rb +19 -0
- data/lib/rubik/ast/c_extractor.rb +114 -0
- data/lib/rubik/ast/cache_store.rb +191 -0
- data/lib/rubik/ast/extractor.rb +59 -0
- data/lib/rubik/ast/fallback_cleaner.rb +145 -0
- data/lib/rubik/ast/go_extractor.rb +148 -0
- data/lib/rubik/ast/graph.rb +272 -0
- data/lib/rubik/ast/python_extractor.rb +120 -0
- data/lib/rubik/ast/ruby_extractor.rb +260 -0
- data/lib/rubik/ast/rust_extractor.rb +127 -0
- data/lib/rubik/ast/symbol.rb +103 -0
- data/lib/rubik/ast/treesitter/base_walker.rb +109 -0
- data/lib/rubik/ast/treesitter/c_walker.rb +122 -0
- data/lib/rubik/ast/treesitter/go_walker.rb +120 -0
- data/lib/rubik/ast/treesitter/python_walker.rb +107 -0
- data/lib/rubik/ast/treesitter/rust_walker.rb +130 -0
- data/lib/rubik/ast/treesitter/typescript_walker.rb +182 -0
- data/lib/rubik/ast/treesitter/zig_walker.rb +109 -0
- data/lib/rubik/ast/treesitter_extractor.rb +93 -0
- data/lib/rubik/ast/typescript_extractor.rb +283 -0
- data/lib/rubik/ast/zig_extractor.rb +116 -0
- data/lib/rubik/ast.rb +13 -0
- data/lib/rubik/benchmark/runner.rb +224 -0
- data/lib/rubik/benchmark/synth_repo.rb +59 -0
- data/lib/rubik/benchmark.rb +9 -0
- data/lib/rubik/buffer/diff.rb +43 -0
- data/lib/rubik/buffer/manager.rb +427 -0
- data/lib/rubik/buffer/path_validator.rb +60 -0
- data/lib/rubik/buffer/snapshot.rb +99 -0
- data/lib/rubik/buffer.rb +10 -0
- data/lib/rubik/coeffects.rb +129 -0
- data/lib/rubik/concurrency/boundary.rb +74 -0
- data/lib/rubik/concurrency/dns_pool.rb +68 -0
- data/lib/rubik/concurrency/errors.rb +17 -0
- data/lib/rubik/concurrency/event_loop.rb +441 -0
- data/lib/rubik/concurrency/join.rb +55 -0
- data/lib/rubik/concurrency/process_waiter.rb +55 -0
- data/lib/rubik/concurrency/scheduler.rb +84 -0
- data/lib/rubik/concurrency/timers.rb +109 -0
- data/lib/rubik/concurrency.rb +132 -0
- data/lib/rubik/config/deep_ops.rb +80 -0
- data/lib/rubik/config/defaults.rb +248 -0
- data/lib/rubik/config/model_catalog.rb +93 -0
- data/lib/rubik/config/store.rb +155 -0
- data/lib/rubik/config.rb +216 -0
- data/lib/rubik/context/git_info.rb +84 -0
- data/lib/rubik/context/git_lister.rb +37 -0
- data/lib/rubik/context/index.rb +134 -0
- data/lib/rubik/context/repo_context_reader.rb +121 -0
- data/lib/rubik/context/scanner.rb +439 -0
- data/lib/rubik/context.rb +11 -0
- data/lib/rubik/diagnostics/collector.rb +200 -0
- data/lib/rubik/diagnostics/entry.rb +60 -0
- data/lib/rubik/diagnostics/parser.rb +107 -0
- data/lib/rubik/diagnostics.rb +10 -0
- data/lib/rubik/event_bus.rb +56 -0
- data/lib/rubik/kernel/dispatcher.rb +171 -0
- data/lib/rubik/kernel/dmesg.rb +116 -0
- data/lib/rubik/kernel/init.rb +177 -0
- data/lib/rubik/kernel/record.rb +85 -0
- data/lib/rubik/kernel/unit.rb +160 -0
- data/lib/rubik/llm/anthropic.rb +202 -0
- data/lib/rubik/llm/chunk_assembler.rb +285 -0
- data/lib/rubik/llm/client.rb +526 -0
- data/lib/rubik/llm/formatter.rb +450 -0
- data/lib/rubik/llm/message_sanitizer.rb +87 -0
- data/lib/rubik/llm/ollama.rb +167 -0
- data/lib/rubik/llm/openai.rb +21 -0
- data/lib/rubik/llm/openai_compatible.rb +216 -0
- data/lib/rubik/llm/provider.rb +125 -0
- data/lib/rubik/llm/registry.rb +336 -0
- data/lib/rubik/llm/router.rb +100 -0
- data/lib/rubik/llm.rb +18 -0
- data/lib/rubik/log.rb +38 -0
- data/lib/rubik/lsp/client.rb +475 -0
- data/lib/rubik/lsp/manager.rb +366 -0
- data/lib/rubik/lsp.rb +9 -0
- data/lib/rubik/mcp/boot_lifecycle.rb +114 -0
- data/lib/rubik/mcp/client.rb +169 -0
- data/lib/rubik/mcp/errors.rb +11 -0
- data/lib/rubik/mcp/manager.rb +619 -0
- data/lib/rubik/mcp/proxy_tool.rb +104 -0
- data/lib/rubik/mcp/transport/stdio.rb +255 -0
- data/lib/rubik/mcp.rb +12 -0
- data/lib/rubik/orchestrator/activity_log.rb +75 -0
- data/lib/rubik/orchestrator/auto_apply.rb +166 -0
- data/lib/rubik/orchestrator/budget_manager.rb +97 -0
- data/lib/rubik/orchestrator/continuation_manager.rb +161 -0
- data/lib/rubik/orchestrator/finish_message_selector.rb +55 -0
- data/lib/rubik/orchestrator/goal_ledger/drift.rb +71 -0
- data/lib/rubik/orchestrator/goal_ledger/ledger.rb +399 -0
- data/lib/rubik/orchestrator/goal_ledger/observation_projection.rb +179 -0
- data/lib/rubik/orchestrator/goal_ledger/patch.rb +268 -0
- data/lib/rubik/orchestrator/goal_ledger/schema.rb +123 -0
- data/lib/rubik/orchestrator/goal_ledger/validator.rb +361 -0
- data/lib/rubik/orchestrator/goal_ledger/verifier.rb +129 -0
- data/lib/rubik/orchestrator/goal_ledger/verify_spec.rb +50 -0
- data/lib/rubik/orchestrator/goal_ledger.rb +65 -0
- data/lib/rubik/orchestrator/goal_state.rb +184 -0
- data/lib/rubik/orchestrator/goal_wait.rb +125 -0
- data/lib/rubik/orchestrator/loop.rb +388 -0
- data/lib/rubik/orchestrator/lsp_feedback.rb +55 -0
- data/lib/rubik/orchestrator/maestro_gate.rb +65 -0
- data/lib/rubik/orchestrator/provider_recovery.rb +90 -0
- data/lib/rubik/orchestrator/provider_resolver.rb +69 -0
- data/lib/rubik/orchestrator/shell_deps.rb +85 -0
- data/lib/rubik/orchestrator/step_executor.rb +328 -0
- data/lib/rubik/orchestrator/stream_manager.rb +118 -0
- data/lib/rubik/orchestrator/subagent/activation.rb +65 -0
- data/lib/rubik/orchestrator/subagent/budget.rb +108 -0
- data/lib/rubik/orchestrator/subagent/completion.rb +61 -0
- data/lib/rubik/orchestrator/subagent/concurrency_gate.rb +37 -0
- data/lib/rubik/orchestrator/subagent/conversation_id.rb +67 -0
- data/lib/rubik/orchestrator/subagent/dependency.rb +398 -0
- data/lib/rubik/orchestrator/subagent/instance.rb +384 -0
- data/lib/rubik/orchestrator/subagent/lifecycle.rb +118 -0
- data/lib/rubik/orchestrator/subagent/messaging.rb +61 -0
- data/lib/rubik/orchestrator/subagent/narration.rb +125 -0
- data/lib/rubik/orchestrator/subagent/notices.rb +209 -0
- data/lib/rubik/orchestrator/subagent/questioner.rb +80 -0
- data/lib/rubik/orchestrator/subagent/reaper.rb +98 -0
- data/lib/rubik/orchestrator/subagent/resilience.rb +70 -0
- data/lib/rubik/orchestrator/subagent/reuse.rb +127 -0
- data/lib/rubik/orchestrator/subagent/scoped_registry.rb +79 -0
- data/lib/rubik/orchestrator/subagent/step_runner.rb +240 -0
- data/lib/rubik/orchestrator/subagent/storage_id.rb +26 -0
- data/lib/rubik/orchestrator/subagent/supervisor.rb +327 -0
- data/lib/rubik/orchestrator/subagent/transcript_formatter.rb +141 -0
- data/lib/rubik/orchestrator/subagent/type_registry.rb +199 -0
- data/lib/rubik/orchestrator/subagent_notice_inbox.rb +80 -0
- data/lib/rubik/orchestrator/tool_call_id.rb +47 -0
- data/lib/rubik/orchestrator/tool_circuit_breaker.rb +91 -0
- data/lib/rubik/orchestrator/tool_history.rb +39 -0
- data/lib/rubik/orchestrator/turn_executor/subagent_channel.rb +102 -0
- data/lib/rubik/orchestrator/turn_executor/user_channel.rb +172 -0
- data/lib/rubik/orchestrator/turn_executor.rb +376 -0
- data/lib/rubik/orchestrator.rb +11 -0
- data/lib/rubik/platform/filesystem.rb +64 -0
- data/lib/rubik/platform/io_drain.rb +53 -0
- data/lib/rubik/platform/path.rb +210 -0
- data/lib/rubik/platform/process.rb +38 -0
- data/lib/rubik/platform/sanitize.rb +157 -0
- data/lib/rubik/platform.rb +113 -0
- data/lib/rubik/plugin/dsl.rb +51 -0
- data/lib/rubik/plugin/manager.rb +247 -0
- data/lib/rubik/plugin/storage.rb +195 -0
- data/lib/rubik/plugin.rb +10 -0
- data/lib/rubik/prompt/context_builder.rb +377 -0
- data/lib/rubik/prompt/context_compactor.rb +326 -0
- data/lib/rubik/prompt/conversation_rows.rb +74 -0
- data/lib/rubik/prompt/doctrine.rb +160 -0
- data/lib/rubik/prompt/emergency_compactor.rb +81 -0
- data/lib/rubik/prompt/services.rb +60 -0
- data/lib/rubik/prompt/token_estimator.rb +63 -0
- data/lib/rubik/prompt/tool_teachings.rb +78 -0
- data/lib/rubik/prompt.rb +13 -0
- data/lib/rubik/repair/loop.rb +95 -0
- data/lib/rubik/repair.rb +8 -0
- data/lib/rubik/repl/ast_warmup.rb +129 -0
- data/lib/rubik/repl/command_context.rb +214 -0
- data/lib/rubik/repl/command_parser.rb +48 -0
- data/lib/rubik/repl/commands/agent.rb +30 -0
- data/lib/rubik/repl/commands/agent_context.rb +289 -0
- data/lib/rubik/repl/commands/agent_execution.rb +342 -0
- data/lib/rubik/repl/commands/agent_models.rb +212 -0
- data/lib/rubik/repl/commands/agent_providers.rb +232 -0
- data/lib/rubik/repl/commands/base.rb +55 -0
- data/lib/rubik/repl/commands/buffer.rb +124 -0
- data/lib/rubik/repl/commands/dmesg.rb +87 -0
- data/lib/rubik/repl/commands/lsp.rb +235 -0
- data/lib/rubik/repl/commands/mcp.rb +194 -0
- data/lib/rubik/repl/commands/plugins.rb +208 -0
- data/lib/rubik/repl/commands/pruner.rb +71 -0
- data/lib/rubik/repl/commands/session.rb +183 -0
- data/lib/rubik/repl/commands/skills.rb +111 -0
- data/lib/rubik/repl/commands/storage.rb +85 -0
- data/lib/rubik/repl/commands/subagents.rb +195 -0
- data/lib/rubik/repl/commands/system.rb +85 -0
- data/lib/rubik/repl/commands/tasks.rb +120 -0
- data/lib/rubik/repl/commands/workspace.rb +443 -0
- data/lib/rubik/repl/commands.rb +44 -0
- data/lib/rubik/repl/composition.rb +32 -0
- data/lib/rubik/repl/config_wiring.rb +142 -0
- data/lib/rubik/repl/confirmations.rb +56 -0
- data/lib/rubik/repl/hooks.rb +47 -0
- data/lib/rubik/repl/input_execution.rb +323 -0
- data/lib/rubik/repl/input_loop.rb +213 -0
- data/lib/rubik/repl/interrupt_handler.rb +217 -0
- data/lib/rubik/repl/lifecycle.rb +410 -0
- data/lib/rubik/repl/paste_support.rb +88 -0
- data/lib/rubik/repl/pending_notice_queue.rb +60 -0
- data/lib/rubik/repl/presentation.rb +132 -0
- data/lib/rubik/repl/pruner/brief.rb +101 -0
- data/lib/rubik/repl/pruner/gate.rb +279 -0
- data/lib/rubik/repl/pruner/harness.rb +236 -0
- data/lib/rubik/repl/pruner/rules.rb +316 -0
- data/lib/rubik/repl/pruner/service.rb +82 -0
- data/lib/rubik/repl/pruner.rb +39 -0
- data/lib/rubik/repl/service_catalog.rb +374 -0
- data/lib/rubik/repl/setup_factories.rb +66 -0
- data/lib/rubik/repl/shell.rb +231 -0
- data/lib/rubik/repl/step_formatter.rb +324 -0
- data/lib/rubik/repl/tee_io.rb +105 -0
- data/lib/rubik/repl/terminal_guard.rb +157 -0
- data/lib/rubik/repl/turn_recorder.rb +234 -0
- data/lib/rubik/repl.rb +14 -0
- data/lib/rubik/runtime/ast_service.rb +78 -0
- data/lib/rubik/runtime/context.rb +335 -0
- data/lib/rubik/runtime/diagnostic_engine.rb +188 -0
- data/lib/rubik/runtime/discovery.rb +342 -0
- data/lib/rubik/runtime/fiber.rb +142 -0
- data/lib/rubik/runtime/fiber_ordering.rb +52 -0
- data/lib/rubik/runtime/fiber_retry_gate.rb +84 -0
- data/lib/rubik/runtime/index_service.rb +58 -0
- data/lib/rubik/runtime/loop_service.rb +73 -0
- data/lib/rubik/runtime/manager.rb +241 -0
- data/lib/rubik/runtime/overlay_storage.rb +481 -0
- data/lib/rubik/runtime/service_definition.rb +54 -0
- data/lib/rubik/runtime/service_registration.rb +33 -0
- data/lib/rubik/runtime/task_manager.rb +302 -0
- data/lib/rubik/runtime.rb +19 -0
- data/lib/rubik/sandbox/driver/base.rb +30 -0
- data/lib/rubik/sandbox/driver/bubblewrap.rb +111 -0
- data/lib/rubik/sandbox/driver/docker.rb +113 -0
- data/lib/rubik/sandbox/driver/podman.rb +98 -0
- data/lib/rubik/sandbox/driver/process.rb +190 -0
- data/lib/rubik/sandbox/driver.rb +91 -0
- data/lib/rubik/sandbox/runner.rb +218 -0
- data/lib/rubik/sandbox/shell_resolver.rb +178 -0
- data/lib/rubik/sandbox.rb +8 -0
- data/lib/rubik/security/blocked_guidance.rb +187 -0
- data/lib/rubik/security/classifier/classifier.rb +440 -0
- data/lib/rubik/security/classifier/commands/agent-browser.yaml +9 -0
- data/lib/rubik/security/classifier/commands/apt.yaml +25 -0
- data/lib/rubik/security/classifier/commands/awk.yaml +10 -0
- data/lib/rubik/security/classifier/commands/basename.yaml +7 -0
- data/lib/rubik/security/classifier/commands/bash-classify.yaml +8 -0
- data/lib/rubik/security/classifier/commands/bash.yaml +20 -0
- data/lib/rubik/security/classifier/commands/black.yaml +12 -0
- data/lib/rubik/security/classifier/commands/brew.yaml +31 -0
- data/lib/rubik/security/classifier/commands/builtin.yaml +8 -0
- data/lib/rubik/security/classifier/commands/cargo.yaml +114 -0
- data/lib/rubik/security/classifier/commands/cat.yaml +7 -0
- data/lib/rubik/security/classifier/commands/chgrp.yaml +10 -0
- data/lib/rubik/security/classifier/commands/chmod.yaml +10 -0
- data/lib/rubik/security/classifier/commands/chown.yaml +10 -0
- data/lib/rubik/security/classifier/commands/column.yaml +7 -0
- data/lib/rubik/security/classifier/commands/comm.yaml +7 -0
- data/lib/rubik/security/classifier/commands/command.yaml +12 -0
- data/lib/rubik/security/classifier/commands/cp.yaml +7 -0
- data/lib/rubik/security/classifier/commands/crystal.yaml +63 -0
- data/lib/rubik/security/classifier/commands/curl.yaml +30 -0
- data/lib/rubik/security/classifier/commands/cut.yaml +7 -0
- data/lib/rubik/security/classifier/commands/date.yaml +10 -0
- data/lib/rubik/security/classifier/commands/df.yaml +7 -0
- data/lib/rubik/security/classifier/commands/diff.yaml +7 -0
- data/lib/rubik/security/classifier/commands/dig.yaml +7 -0
- data/lib/rubik/security/classifier/commands/dirname.yaml +7 -0
- data/lib/rubik/security/classifier/commands/docker.yaml +177 -0
- data/lib/rubik/security/classifier/commands/du.yaml +7 -0
- data/lib/rubik/security/classifier/commands/echo.yaml +7 -0
- data/lib/rubik/security/classifier/commands/env.yaml +15 -0
- data/lib/rubik/security/classifier/commands/eslint.yaml +12 -0
- data/lib/rubik/security/classifier/commands/eval.yaml +8 -0
- data/lib/rubik/security/classifier/commands/exec.yaml +12 -0
- data/lib/rubik/security/classifier/commands/expand.yaml +7 -0
- data/lib/rubik/security/classifier/commands/false.yaml +7 -0
- data/lib/rubik/security/classifier/commands/file.yaml +7 -0
- data/lib/rubik/security/classifier/commands/find.yaml +25 -0
- data/lib/rubik/security/classifier/commands/fmt.yaml +7 -0
- data/lib/rubik/security/classifier/commands/fold.yaml +7 -0
- data/lib/rubik/security/classifier/commands/free.yaml +7 -0
- data/lib/rubik/security/classifier/commands/gawk.yaml +10 -0
- data/lib/rubik/security/classifier/commands/gcloud.yaml +979 -0
- data/lib/rubik/security/classifier/commands/gh.yaml +615 -0
- data/lib/rubik/security/classifier/commands/git.yaml +524 -0
- data/lib/rubik/security/classifier/commands/glab-discussion.yaml +21 -0
- data/lib/rubik/security/classifier/commands/glab.yaml +807 -0
- data/lib/rubik/security/classifier/commands/go.yaml +44 -0
- data/lib/rubik/security/classifier/commands/gog.yaml +983 -0
- data/lib/rubik/security/classifier/commands/gradle.yaml +92 -0
- data/lib/rubik/security/classifier/commands/gradlew.yaml +92 -0
- data/lib/rubik/security/classifier/commands/grep.yaml +7 -0
- data/lib/rubik/security/classifier/commands/groups.yaml +7 -0
- data/lib/rubik/security/classifier/commands/gunzip.yaml +8 -0
- data/lib/rubik/security/classifier/commands/gws.yaml +211 -0
- data/lib/rubik/security/classifier/commands/gzip.yaml +7 -0
- data/lib/rubik/security/classifier/commands/hadolint.yaml +9 -0
- data/lib/rubik/security/classifier/commands/halt.yaml +8 -0
- data/lib/rubik/security/classifier/commands/head.yaml +7 -0
- data/lib/rubik/security/classifier/commands/helm.yaml +129 -0
- data/lib/rubik/security/classifier/commands/hostname.yaml +7 -0
- data/lib/rubik/security/classifier/commands/id.yaml +7 -0
- data/lib/rubik/security/classifier/commands/install.yaml +7 -0
- data/lib/rubik/security/classifier/commands/isort.yaml +14 -0
- data/lib/rubik/security/classifier/commands/java.yaml +35 -0
- data/lib/rubik/security/classifier/commands/javac.yaml +39 -0
- data/lib/rubik/security/classifier/commands/join.yaml +7 -0
- data/lib/rubik/security/classifier/commands/journalctl.yaml +14 -0
- data/lib/rubik/security/classifier/commands/jq.yaml +7 -0
- data/lib/rubik/security/classifier/commands/kill.yaml +7 -0
- data/lib/rubik/security/classifier/commands/killall.yaml +7 -0
- data/lib/rubik/security/classifier/commands/kubectl.yaml +211 -0
- data/lib/rubik/security/classifier/commands/less.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ln.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ls.yaml +7 -0
- data/lib/rubik/security/classifier/commands/make.yaml +21 -0
- data/lib/rubik/security/classifier/commands/md5sum.yaml +7 -0
- data/lib/rubik/security/classifier/commands/mise.yaml +579 -0
- data/lib/rubik/security/classifier/commands/mkdir.yaml +8 -0
- data/lib/rubik/security/classifier/commands/more.yaml +7 -0
- data/lib/rubik/security/classifier/commands/mv.yaml +7 -0
- data/lib/rubik/security/classifier/commands/mvn.yaml +83 -0
- data/lib/rubik/security/classifier/commands/mvnw.yaml +83 -0
- data/lib/rubik/security/classifier/commands/mypy.yaml +9 -0
- data/lib/rubik/security/classifier/commands/netstat.yaml +7 -0
- data/lib/rubik/security/classifier/commands/nice.yaml +11 -0
- data/lib/rubik/security/classifier/commands/nl.yaml +7 -0
- data/lib/rubik/security/classifier/commands/node.yaml +20 -0
- data/lib/rubik/security/classifier/commands/nohup.yaml +9 -0
- data/lib/rubik/security/classifier/commands/npm.yaml +71 -0
- data/lib/rubik/security/classifier/commands/npx.yaml +42 -0
- data/lib/rubik/security/classifier/commands/nslookup.yaml +7 -0
- data/lib/rubik/security/classifier/commands/paste.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ping.yaml +7 -0
- data/lib/rubik/security/classifier/commands/pip.yaml +33 -0
- data/lib/rubik/security/classifier/commands/pip3.yaml +33 -0
- data/lib/rubik/security/classifier/commands/pipx.yaml +33 -0
- data/lib/rubik/security/classifier/commands/pkill.yaml +7 -0
- data/lib/rubik/security/classifier/commands/pnpm.yaml +346 -0
- data/lib/rubik/security/classifier/commands/poetry.yaml +97 -0
- data/lib/rubik/security/classifier/commands/poweroff.yaml +8 -0
- data/lib/rubik/security/classifier/commands/prettier.yaml +13 -0
- data/lib/rubik/security/classifier/commands/printenv.yaml +7 -0
- data/lib/rubik/security/classifier/commands/printf.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ps.yaml +7 -0
- data/lib/rubik/security/classifier/commands/pup.yaml +882 -0
- data/lib/rubik/security/classifier/commands/pwd.yaml +7 -0
- data/lib/rubik/security/classifier/commands/pytest.yaml +7 -0
- data/lib/rubik/security/classifier/commands/python.yaml +9 -0
- data/lib/rubik/security/classifier/commands/python3.yaml +9 -0
- data/lib/rubik/security/classifier/commands/read.yaml +7 -0
- data/lib/rubik/security/classifier/commands/readlink.yaml +7 -0
- data/lib/rubik/security/classifier/commands/realpath.yaml +7 -0
- data/lib/rubik/security/classifier/commands/reboot.yaml +8 -0
- data/lib/rubik/security/classifier/commands/rev.yaml +7 -0
- data/lib/rubik/security/classifier/commands/rg.yaml +7 -0
- data/lib/rubik/security/classifier/commands/rm.yaml +8 -0
- data/lib/rubik/security/classifier/commands/rsync.yaml +12 -0
- data/lib/rubik/security/classifier/commands/rtk.yaml +140 -0
- data/lib/rubik/security/classifier/commands/ruff.yaml +29 -0
- data/lib/rubik/security/classifier/commands/rustfmt.yaml +14 -0
- data/lib/rubik/security/classifier/commands/scp.yaml +7 -0
- data/lib/rubik/security/classifier/commands/sed.yaml +14 -0
- data/lib/rubik/security/classifier/commands/seq.yaml +7 -0
- data/lib/rubik/security/classifier/commands/sh.yaml +14 -0
- data/lib/rubik/security/classifier/commands/sha1sum.yaml +7 -0
- data/lib/rubik/security/classifier/commands/sha256sum.yaml +7 -0
- data/lib/rubik/security/classifier/commands/shellcheck.yaml +10 -0
- data/lib/rubik/security/classifier/commands/shutdown.yaml +8 -0
- data/lib/rubik/security/classifier/commands/slack.yaml +113 -0
- data/lib/rubik/security/classifier/commands/sleep.yaml +8 -0
- data/lib/rubik/security/classifier/commands/snip.yaml +79 -0
- data/lib/rubik/security/classifier/commands/sort.yaml +10 -0
- data/lib/rubik/security/classifier/commands/ss.yaml +7 -0
- data/lib/rubik/security/classifier/commands/ssh.yaml +41 -0
- data/lib/rubik/security/classifier/commands/stat.yaml +7 -0
- data/lib/rubik/security/classifier/commands/stdbuf.yaml +15 -0
- data/lib/rubik/security/classifier/commands/strace.yaml +20 -0
- data/lib/rubik/security/classifier/commands/strings.yaml +11 -0
- data/lib/rubik/security/classifier/commands/sudo.yaml +24 -0
- data/lib/rubik/security/classifier/commands/systemctl.yaml +58 -0
- data/lib/rubik/security/classifier/commands/systemd-run.yaml +30 -0
- data/lib/rubik/security/classifier/commands/tac.yaml +7 -0
- data/lib/rubik/security/classifier/commands/tail.yaml +7 -0
- data/lib/rubik/security/classifier/commands/tar.yaml +25 -0
- data/lib/rubik/security/classifier/commands/tee.yaml +8 -0
- data/lib/rubik/security/classifier/commands/terraform.yaml +70 -0
- data/lib/rubik/security/classifier/commands/test.yaml +7 -0
- data/lib/rubik/security/classifier/commands/time.yaml +8 -0
- data/lib/rubik/security/classifier/commands/timeout.yaml +18 -0
- data/lib/rubik/security/classifier/commands/top.yaml +7 -0
- data/lib/rubik/security/classifier/commands/touch.yaml +8 -0
- data/lib/rubik/security/classifier/commands/tr.yaml +7 -0
- data/lib/rubik/security/classifier/commands/traceroute.yaml +7 -0
- data/lib/rubik/security/classifier/commands/true.yaml +7 -0
- data/lib/rubik/security/classifier/commands/tsc.yaml +16 -0
- data/lib/rubik/security/classifier/commands/type.yaml +7 -0
- data/lib/rubik/security/classifier/commands/uname.yaml +7 -0
- data/lib/rubik/security/classifier/commands/unexpand.yaml +7 -0
- data/lib/rubik/security/classifier/commands/uniq.yaml +7 -0
- data/lib/rubik/security/classifier/commands/unzip.yaml +10 -0
- data/lib/rubik/security/classifier/commands/uptime.yaml +7 -0
- data/lib/rubik/security/classifier/commands/uv.yaml +156 -0
- data/lib/rubik/security/classifier/commands/wait.yaml +11 -0
- data/lib/rubik/security/classifier/commands/wc.yaml +7 -0
- data/lib/rubik/security/classifier/commands/wget.yaml +7 -0
- data/lib/rubik/security/classifier/commands/whereis.yaml +7 -0
- data/lib/rubik/security/classifier/commands/which.yaml +7 -0
- data/lib/rubik/security/classifier/commands/whoami.yaml +7 -0
- data/lib/rubik/security/classifier/commands/xargs.yaml +26 -0
- data/lib/rubik/security/classifier/commands/xmllint.yaml +7 -0
- data/lib/rubik/security/classifier/commands/yarn.yaml +72 -0
- data/lib/rubik/security/classifier/commands/yes.yaml +7 -0
- data/lib/rubik/security/classifier/commands/yq.yaml +10 -0
- data/lib/rubik/security/classifier/commands/zip.yaml +8 -0
- data/lib/rubik/security/classifier/commands/zsh.yaml +14 -0
- data/lib/rubik/security/classifier/database.rb +216 -0
- data/lib/rubik/security/classifier/matcher/delegation.rb +190 -0
- data/lib/rubik/security/classifier/matcher/options.rb +245 -0
- data/lib/rubik/security/classifier/matcher/subcommands.rb +103 -0
- data/lib/rubik/security/classifier/matcher.rb +249 -0
- data/lib/rubik/security/classifier/models.rb +170 -0
- data/lib/rubik/security/classifier/parser.rb +23 -0
- data/lib/rubik/security/classifier/parsers/bash.rb +280 -0
- data/lib/rubik/security/classifier/parsers/bash_extractors.rb +221 -0
- data/lib/rubik/security/classifier/parsers/powershell.rb +185 -0
- data/lib/rubik/security/classifier/pwsh_rules.rb +224 -0
- data/lib/rubik/security/classifier.rb +25 -0
- data/lib/rubik/security/errors.rb +39 -0
- data/lib/rubik/security/guard.rb +954 -0
- data/lib/rubik/security/redactor.rb +89 -0
- data/lib/rubik/security.rb +11 -0
- data/lib/rubik/session/manager.rb +331 -0
- data/lib/rubik/session/recorder.rb +149 -0
- data/lib/rubik/session/replayer.rb +65 -0
- data/lib/rubik/session.rb +10 -0
- data/lib/rubik/skills/frontmatter.rb +41 -0
- data/lib/rubik/skills/manager.rb +223 -0
- data/lib/rubik/skills/registry.rb +64 -0
- data/lib/rubik/skills/skill.rb +33 -0
- data/lib/rubik/skills/storage.rb +77 -0
- data/lib/rubik/skills/validator.rb +116 -0
- data/lib/rubik/skills.rb +20 -0
- data/lib/rubik/storage/folding.rb +23 -0
- data/lib/rubik/storage/layout.rb +145 -0
- data/lib/rubik/storage/render.rb +97 -0
- data/lib/rubik/storage/service.rb +42 -0
- data/lib/rubik/storage/store.rb +514 -0
- data/lib/rubik/storage/transcript_indexer.rb +116 -0
- data/lib/rubik/storage/uri_reader.rb +168 -0
- data/lib/rubik/storage/vakanuvis/annal_writer.rb +92 -0
- data/lib/rubik/storage/vakanuvis/distill_gate.rb +323 -0
- data/lib/rubik/storage/vakanuvis/gate.rb +250 -0
- data/lib/rubik/storage/vakanuvis/harness.rb +421 -0
- data/lib/rubik/storage/vakanuvis/memory_steward.rb +163 -0
- data/lib/rubik/storage/vakanuvis/service.rb +69 -0
- data/lib/rubik/storage/vakanuvis/vocabulary.rb +99 -0
- data/lib/rubik/storage/vakanuvis/wiki_skeleton.rb +67 -0
- data/lib/rubik/storage/vakanuvis.rb +77 -0
- data/lib/rubik/storage.rb +29 -0
- data/lib/rubik/tools/base.rb +107 -0
- data/lib/rubik/tools/builtins/dmesg.rb +74 -0
- data/lib/rubik/tools/builtins/exec.rb +293 -0
- data/lib/rubik/tools/builtins/files.rb +354 -0
- data/lib/rubik/tools/builtins/goal_ledger.rb +89 -0
- data/lib/rubik/tools/builtins/grep.rb +281 -0
- data/lib/rubik/tools/builtins/interactive.rb +175 -0
- data/lib/rubik/tools/builtins/lifecycle.rb +359 -0
- data/lib/rubik/tools/builtins/lsp.rb +283 -0
- data/lib/rubik/tools/builtins/pruner.rb +88 -0
- data/lib/rubik/tools/builtins/runtime.rb +120 -0
- data/lib/rubik/tools/builtins/send_message.rb +109 -0
- data/lib/rubik/tools/builtins/show_file.rb +84 -0
- data/lib/rubik/tools/builtins/skill.rb +72 -0
- data/lib/rubik/tools/builtins/storage.rb +232 -0
- data/lib/rubik/tools/builtins/subagent.rb +356 -0
- data/lib/rubik/tools/builtins/subagent_lifecycle.rb +72 -0
- data/lib/rubik/tools/builtins/symbols.rb +209 -0
- data/lib/rubik/tools/builtins/synthesis.rb +314 -0
- data/lib/rubik/tools/builtins/task.rb +168 -0
- data/lib/rubik/tools/builtins/unsynthesize.rb +111 -0
- data/lib/rubik/tools/builtins/vakanuvis.rb +161 -0
- data/lib/rubik/tools/builtins.rb +30 -0
- data/lib/rubik/tools/catalog.rb +70 -0
- data/lib/rubik/tools/declaration.rb +90 -0
- data/lib/rubik/tools/diagnostic_reporter.rb +283 -0
- data/lib/rubik/tools/fuzzy_matcher.rb +172 -0
- data/lib/rubik/tools/native_search.rb +180 -0
- data/lib/rubik/tools/output.rb +54 -0
- data/lib/rubik/tools/preflight_runner.rb +209 -0
- data/lib/rubik/tools/registry.rb +247 -0
- data/lib/rubik/tools/result.rb +71 -0
- data/lib/rubik/tools/services.rb +96 -0
- data/lib/rubik/tools/truncator.rb +78 -0
- data/lib/rubik/tools/uri_resolver.rb +195 -0
- data/lib/rubik/tools.rb +14 -0
- data/lib/rubik/ui/approvals.rb +52 -0
- data/lib/rubik/ui/banner.rb +43 -0
- data/lib/rubik/ui/capability.rb +122 -0
- data/lib/rubik/ui/dialog.rb +106 -0
- data/lib/rubik/ui/diff_viewer.rb +78 -0
- data/lib/rubik/ui/frame.rb +95 -0
- data/lib/rubik/ui/markdown_stream.rb +107 -0
- data/lib/rubik/ui/panel.rb +98 -0
- data/lib/rubik/ui/renderer.rb +206 -0
- data/lib/rubik/ui/spinner.rb +120 -0
- data/lib/rubik/ui/statusline.rb +134 -0
- data/lib/rubik/ui/theme.rb +118 -0
- data/lib/rubik/ui.rb +57 -0
- data/lib/rubik/uris.rb +101 -0
- data/lib/rubik/version.rb +5 -0
- data/lib/rubik.rb +65 -0
- metadata +770 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../platform"
|
|
4
|
+
|
|
5
|
+
module Rubik
|
|
6
|
+
module Runtime
|
|
7
|
+
class ExecutionFailure
|
|
8
|
+
attr_reader :cmd, :exit_code, :stdout, :stderr, :duration, :category, :suggestion, :metadata, :timestamp
|
|
9
|
+
|
|
10
|
+
def initialize(cmd:, exit_code:, stdout: "", stderr: "", duration: 0.0, category: :unknown_failure, suggestion: nil, metadata: {})
|
|
11
|
+
@cmd = cmd.to_s
|
|
12
|
+
@exit_code = exit_code.to_i
|
|
13
|
+
@stdout = stdout.to_s
|
|
14
|
+
@stderr = stderr.to_s
|
|
15
|
+
@duration = duration.to_f
|
|
16
|
+
@category = category.to_sym
|
|
17
|
+
@suggestion = suggestion.to_s
|
|
18
|
+
@metadata = metadata || {}
|
|
19
|
+
@timestamp = begin
|
|
20
|
+
Time.now.utc.iso8601
|
|
21
|
+
rescue NoMethodError
|
|
22
|
+
Time.now.to_s
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_h
|
|
27
|
+
{
|
|
28
|
+
cmd: @cmd,
|
|
29
|
+
exit_code: @exit_code,
|
|
30
|
+
stdout: @stdout,
|
|
31
|
+
stderr: @stderr,
|
|
32
|
+
duration: @duration,
|
|
33
|
+
category: @category,
|
|
34
|
+
suggestion: @suggestion,
|
|
35
|
+
metadata: @metadata,
|
|
36
|
+
timestamp: @timestamp
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def format_for_llm
|
|
41
|
+
lines = [
|
|
42
|
+
"=== Runtime Execution Diagnostic ===",
|
|
43
|
+
"• Command : #{@cmd}",
|
|
44
|
+
"• Exit Code : #{@exit_code}",
|
|
45
|
+
"• Duration : #{@duration.round(4)}s",
|
|
46
|
+
"• Category : #{@category.to_s.upcase}"
|
|
47
|
+
]
|
|
48
|
+
lines << "• Stderr : #{@stderr.strip}" unless @stderr.strip.empty?
|
|
49
|
+
lines << "• Stdout : #{@stdout.strip}" if @stderr.strip.empty? && !@stdout.strip.empty?
|
|
50
|
+
lines << "• Suggestion : #{@suggestion}" unless @suggestion.strip.empty?
|
|
51
|
+
lines.join("\n")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
module DiagnosticEngine
|
|
56
|
+
class << self
|
|
57
|
+
def diagnose(cmd:, exit_code:, stdout: "", stderr: "", duration: 0.0, metadata: {})
|
|
58
|
+
cmd_str = cmd.to_s
|
|
59
|
+
stderr_str = stderr.to_s
|
|
60
|
+
stdout_str = stdout.to_s
|
|
61
|
+
combined = "#{stderr_str}\n#{stdout_str}"
|
|
62
|
+
|
|
63
|
+
category = determine_category(cmd_str, exit_code, stderr_str, stdout_str, combined)
|
|
64
|
+
suggestion = generate_suggestion(category, cmd_str, combined)
|
|
65
|
+
|
|
66
|
+
ExecutionFailure.new(
|
|
67
|
+
cmd: cmd_str,
|
|
68
|
+
exit_code: exit_code,
|
|
69
|
+
stdout: stdout_str,
|
|
70
|
+
stderr: stderr_str,
|
|
71
|
+
duration: duration,
|
|
72
|
+
category: category,
|
|
73
|
+
suggestion: suggestion,
|
|
74
|
+
metadata: metadata
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def analyze_process_result(process_result, cmd: nil, metadata: {})
|
|
79
|
+
return nil if process_result.nil?
|
|
80
|
+
|
|
81
|
+
res_cmd = cmd || process_result[:cmd] || "unknown_command"
|
|
82
|
+
diagnose(
|
|
83
|
+
cmd: res_cmd,
|
|
84
|
+
exit_code: process_result[:exit_code] || (process_result[:ok] ? 0 : 1),
|
|
85
|
+
stdout: process_result[:stdout] || "",
|
|
86
|
+
stderr: process_result[:stderr] || (process_result[:ok] ? "" : process_result[:output].to_s),
|
|
87
|
+
duration: process_result[:duration] || 0.0,
|
|
88
|
+
metadata: metadata
|
|
89
|
+
)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Search tools whose ONLY exit-1 meaning is "pattern matched nothing"
|
|
93
|
+
# (session review B8): grep/rg/findstr exit 1 on no match and 2 on a
|
|
94
|
+
# real error, so exit 1 + clean streams is a normal empty result, not
|
|
95
|
+
# an UNKNOWN_FAILURE alarm.
|
|
96
|
+
NO_MATCH_TOOLS = ["grep", "rg", "findstr", "ag", "ack"].freeze
|
|
97
|
+
NO_MATCH_PREFIXES = ["git grep"].freeze
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
def no_match_tool?(cmd)
|
|
102
|
+
tokens = cmd.to_s.split
|
|
103
|
+
return false if tokens.empty?
|
|
104
|
+
|
|
105
|
+
NO_MATCH_TOOLS.include?(tokens.first) || NO_MATCH_PREFIXES.any? { |p| cmd.to_s.start_with?("#{p} ") }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Cargo plugin convention: `cargo <name>` resolves the binary
|
|
109
|
+
# `cargo-<name>` in PATH. A "no such command" from cargo therefore
|
|
110
|
+
# means the plugin is missing — name the exact install command.
|
|
111
|
+
def cargo_plugin_suggestion(cmd, combined)
|
|
112
|
+
return nil unless combined.match?(/no such (?:command|subcommand)/i)
|
|
113
|
+
|
|
114
|
+
tokens = cmd.to_s.split
|
|
115
|
+
return nil unless tokens.shift == "cargo" && tokens.first.to_s.match?(/\A[a-z][\w-]*\z/i)
|
|
116
|
+
|
|
117
|
+
plugin = tokens.first
|
|
118
|
+
"cargo's plugin binary 'cargo-#{plugin}' is not installed. Install it with " \
|
|
119
|
+
"`cargo install cargo-#{plugin}` (e.g. cargo install cargo-nextest), pick a runner that is " \
|
|
120
|
+
"installed (plain `cargo test`), or configure one via repair_runtime_service."
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def determine_category(cmd, exit_code, _stderr, stdout, combined)
|
|
124
|
+
return :success if exit_code.zero?
|
|
125
|
+
return :no_match if exit_code == 1 && no_match_tool?(cmd) && stdout.strip.empty? && !combined.match?(/\b(error|fatal)\b/i)
|
|
126
|
+
|
|
127
|
+
if exit_code == 124 || combined.match?(/(timed out|timeout after|\btimeout\b)/i)
|
|
128
|
+
:timeout
|
|
129
|
+
# The second alternative is the runner-wrapper signature (e.g.
|
|
130
|
+
# cargo without its plugin): the wrapper reports on stderr while
|
|
131
|
+
# the suite produced no output. A genuine failing suite that merely
|
|
132
|
+
# prints the phrase keeps its real category.
|
|
133
|
+
elsif (exit_code == 127 || exit_code == 9009 || exit_code == 49 ||
|
|
134
|
+
combined.match?(/(not recognized|\bnot found\b|command not found|cannot find|bulunamad|\bENOENT\b)/i)) ||
|
|
135
|
+
(stdout.strip.empty? && combined.match?(/no such (?:command|subcommand)/i))
|
|
136
|
+
:missing_binary
|
|
137
|
+
elsif combined.match?(/(permission denied|\bEACCES\b|operation not permitted)/i)
|
|
138
|
+
:permission_denied
|
|
139
|
+
elsif combined.match?(/(unrecognized option|unknown option|invalid option|requires stdio|--stdio|flag provided but not defined|unknown flag)/i)
|
|
140
|
+
:missing_flag_or_option
|
|
141
|
+
elsif combined.match?(
|
|
142
|
+
/(cannot load such file|ModuleNotFoundError|No module named|package .* not found|bundle install|npm install|pnpm install|yarn install)/i
|
|
143
|
+
)
|
|
144
|
+
:dependency_missing
|
|
145
|
+
elsif combined.match?(/(syntax error|unexpected token|compilation error|error\[E\d+\]|parse error|fatal error)/i)
|
|
146
|
+
:syntax_or_compile_error
|
|
147
|
+
elsif combined.match?(/(failures:|assertions|tests failed|failed tests|FAILED \(|FAIL:|\d+ failures, \d+ errors)/i)
|
|
148
|
+
:test_failure
|
|
149
|
+
else
|
|
150
|
+
:unknown_failure
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def generate_suggestion(category, cmd, combined)
|
|
155
|
+
bin = cmd.split.first || "command"
|
|
156
|
+
|
|
157
|
+
case category
|
|
158
|
+
when :no_match
|
|
159
|
+
"'#{cmd.split.first(2).join(' ')}' completed normally: the pattern matched nothing (exit 1 with clean " \
|
|
160
|
+
"output is success for search tools). Refine the pattern or broaden the search path instead of debugging a failure."
|
|
161
|
+
when :missing_binary
|
|
162
|
+
cargo_plugin_suggestion(cmd, combined) ||
|
|
163
|
+
"The executable binary '#{bin}' was not found in PATH or root workspace. " \
|
|
164
|
+
"Install the required tool or use `repair_runtime_service` / `!synthesize` to configure a working alternative."
|
|
165
|
+
when :missing_flag_or_option
|
|
166
|
+
"Command line flags or arguments appear invalid. Check supported flags (e.g. `--stdio`, `stdio`) " \
|
|
167
|
+
"or use `repair_runtime_service` to probe correct options."
|
|
168
|
+
when :dependency_missing
|
|
169
|
+
"Required dependencies are missing. Run appropriate package manager install " \
|
|
170
|
+
"(e.g., `bundle install`, `npm install`, `pip install`) before proceeding."
|
|
171
|
+
when :syntax_or_compile_error
|
|
172
|
+
"A syntax or compilation error occurred. Inspect the reported source file and line numbers to resolve the error."
|
|
173
|
+
when :permission_denied
|
|
174
|
+
"Permission denied for '#{bin}'. Check file permissions (chmod +x) or sandbox security constraints."
|
|
175
|
+
when :timeout
|
|
176
|
+
"Process execution exceeded timeout threshold. Increase timeout limit or investigate potential infinite loops / blocking calls."
|
|
177
|
+
when :test_failure
|
|
178
|
+
"One or more tests failed. Review assertion failure outputs and update the corresponding implementation."
|
|
179
|
+
when :success
|
|
180
|
+
"Command executed successfully."
|
|
181
|
+
else
|
|
182
|
+
"Process exited with non-zero code. Review stderr/stdout traces to investigate cause."
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "shellwords"
|
|
4
|
+
require_relative "../platform"
|
|
5
|
+
|
|
6
|
+
module Rubik
|
|
7
|
+
module Runtime
|
|
8
|
+
module Discovery
|
|
9
|
+
LSP_CANDIDATES = {
|
|
10
|
+
"ruby" => [
|
|
11
|
+
"solargraph stdio",
|
|
12
|
+
"ruby-lsp",
|
|
13
|
+
"typeprof --lsp --stdio"
|
|
14
|
+
],
|
|
15
|
+
"python" => [
|
|
16
|
+
"pyright-langserver --stdio",
|
|
17
|
+
"pylsp",
|
|
18
|
+
"jedi-language-server"
|
|
19
|
+
],
|
|
20
|
+
"rust" => [
|
|
21
|
+
"rust-analyzer"
|
|
22
|
+
],
|
|
23
|
+
"go" => [
|
|
24
|
+
"gopls"
|
|
25
|
+
],
|
|
26
|
+
"zig" => [
|
|
27
|
+
"zls"
|
|
28
|
+
],
|
|
29
|
+
"c" => [
|
|
30
|
+
"clangd"
|
|
31
|
+
],
|
|
32
|
+
"cpp" => [
|
|
33
|
+
"clangd"
|
|
34
|
+
],
|
|
35
|
+
"typescript" => [
|
|
36
|
+
"typescript-language-server --stdio",
|
|
37
|
+
"vtsls --stdio"
|
|
38
|
+
],
|
|
39
|
+
"javascript" => [
|
|
40
|
+
"typescript-language-server --stdio",
|
|
41
|
+
"vtsls --stdio"
|
|
42
|
+
],
|
|
43
|
+
"bash" => [
|
|
44
|
+
"bash-language-server start"
|
|
45
|
+
],
|
|
46
|
+
"crystal" => [
|
|
47
|
+
"crystalline"
|
|
48
|
+
],
|
|
49
|
+
"gleam" => [
|
|
50
|
+
"gleam lsp"
|
|
51
|
+
],
|
|
52
|
+
"vue" => [
|
|
53
|
+
"vue-language-server --stdio",
|
|
54
|
+
"@vue/language-server --stdio",
|
|
55
|
+
"vls"
|
|
56
|
+
]
|
|
57
|
+
}.freeze
|
|
58
|
+
|
|
59
|
+
FORMATTER_CANDIDATES = {
|
|
60
|
+
"ruby" => [
|
|
61
|
+
"rubocop -a",
|
|
62
|
+
"standardrb --fix",
|
|
63
|
+
"rufo"
|
|
64
|
+
],
|
|
65
|
+
"python" => [
|
|
66
|
+
"ruff format",
|
|
67
|
+
"black"
|
|
68
|
+
],
|
|
69
|
+
"rust" => [
|
|
70
|
+
"rustfmt"
|
|
71
|
+
],
|
|
72
|
+
"go" => [
|
|
73
|
+
"gofmt -w",
|
|
74
|
+
"goimports -w"
|
|
75
|
+
],
|
|
76
|
+
"c" => [
|
|
77
|
+
"clang-format -i"
|
|
78
|
+
],
|
|
79
|
+
"cpp" => [
|
|
80
|
+
"clang-format -i"
|
|
81
|
+
],
|
|
82
|
+
"zig" => [
|
|
83
|
+
"zig fmt"
|
|
84
|
+
],
|
|
85
|
+
"typescript" => [
|
|
86
|
+
"prettier --write",
|
|
87
|
+
"biome format --write"
|
|
88
|
+
],
|
|
89
|
+
"javascript" => [
|
|
90
|
+
"prettier --write",
|
|
91
|
+
"biome format --write"
|
|
92
|
+
],
|
|
93
|
+
"node" => [
|
|
94
|
+
"prettier --write",
|
|
95
|
+
"biome format --write"
|
|
96
|
+
],
|
|
97
|
+
"vue" => [
|
|
98
|
+
"prettier --write",
|
|
99
|
+
"biome format --write"
|
|
100
|
+
],
|
|
101
|
+
"crystal" => [
|
|
102
|
+
"crystal tool format"
|
|
103
|
+
]
|
|
104
|
+
}.freeze
|
|
105
|
+
|
|
106
|
+
TEST_RUNNER_MARKERS = {
|
|
107
|
+
"ruby" => [
|
|
108
|
+
{ marker: "Gemfile", candidates: ["bundle exec rspec", "bundle exec rake test", "rake test"] },
|
|
109
|
+
{ marker: "Rakefile", candidates: ["bundle exec rake test", "rake test"] }
|
|
110
|
+
],
|
|
111
|
+
"python" => [
|
|
112
|
+
{ marker: "pyproject.toml", candidates: ["pytest", "python3 -m unittest"] },
|
|
113
|
+
{ marker: "pytest.ini", candidates: ["pytest"] },
|
|
114
|
+
{ marker: "setup.py", candidates: ["pytest", "python3 -m unittest"] },
|
|
115
|
+
{ marker: "requirements.txt", candidates: ["pytest", "python3 -m unittest"] }
|
|
116
|
+
],
|
|
117
|
+
"rust" => [
|
|
118
|
+
{ marker: "Cargo.toml", candidates: ["cargo nextest run", "cargo test"] }
|
|
119
|
+
],
|
|
120
|
+
"go" => [
|
|
121
|
+
{ marker: "go.mod", candidates: ["go test ./..."] }
|
|
122
|
+
],
|
|
123
|
+
"zig" => [
|
|
124
|
+
{ marker: "build.zig", candidates: ["zig build test", "zig test"] }
|
|
125
|
+
],
|
|
126
|
+
"typescript" => [
|
|
127
|
+
{ marker: "pnpm-lock.yaml", candidates: ["pnpm test"] },
|
|
128
|
+
{ marker: "yarn.lock", candidates: ["yarn test"] },
|
|
129
|
+
{ marker: "package-lock.json", candidates: ["npm test"] },
|
|
130
|
+
{ marker: "package.json", candidates: ["npm test", "pnpm test", "yarn test"] }
|
|
131
|
+
],
|
|
132
|
+
"javascript" => [
|
|
133
|
+
{ marker: "pnpm-lock.yaml", candidates: ["pnpm test"] },
|
|
134
|
+
{ marker: "yarn.lock", candidates: ["yarn test"] },
|
|
135
|
+
{ marker: "package-lock.json", candidates: ["npm test"] },
|
|
136
|
+
{ marker: "package.json", candidates: ["npm test", "pnpm test", "yarn test"] }
|
|
137
|
+
],
|
|
138
|
+
"vue" => [
|
|
139
|
+
{ marker: "pnpm-lock.yaml", candidates: ["pnpm test"] },
|
|
140
|
+
{ marker: "yarn.lock", candidates: ["yarn test"] },
|
|
141
|
+
{ marker: "bun.lockb", candidates: ["bun test"] },
|
|
142
|
+
{ marker: "package-lock.json", candidates: ["npm test"] },
|
|
143
|
+
{ marker: "package.json", candidates: ["npm test", "pnpm test", "yarn test", "bun test"] }
|
|
144
|
+
],
|
|
145
|
+
"node" => [
|
|
146
|
+
{ marker: "pnpm-lock.yaml", candidates: ["pnpm test"] },
|
|
147
|
+
{ marker: "yarn.lock", candidates: ["yarn test"] },
|
|
148
|
+
{ marker: "bun.lockb", candidates: ["bun test"] },
|
|
149
|
+
{ marker: "package-lock.json", candidates: ["npm test"] },
|
|
150
|
+
{ marker: "package.json", candidates: ["npm test", "pnpm test", "yarn test", "bun test"] }
|
|
151
|
+
],
|
|
152
|
+
"c" => [
|
|
153
|
+
{ marker: "CMakeLists.txt", candidates: ["ctest", "make test"] },
|
|
154
|
+
{ marker: "Makefile", candidates: ["make test", "ctest"] }
|
|
155
|
+
],
|
|
156
|
+
"crystal" => [
|
|
157
|
+
{ marker: "shard.yml", candidates: ["crystal spec"] },
|
|
158
|
+
{ marker: "shard.lock", candidates: ["crystal spec"] }
|
|
159
|
+
]
|
|
160
|
+
}.freeze
|
|
161
|
+
|
|
162
|
+
DEFAULT_TEST_RUNNERS = {
|
|
163
|
+
"ruby" => "rake test",
|
|
164
|
+
"python" => "pytest",
|
|
165
|
+
"rust" => "cargo test",
|
|
166
|
+
"go" => "go test ./...",
|
|
167
|
+
"zig" => "zig test",
|
|
168
|
+
"c" => "ctest",
|
|
169
|
+
"crystal" => "crystal spec",
|
|
170
|
+
"typescript" => "npm test",
|
|
171
|
+
"javascript" => "npm test",
|
|
172
|
+
"vue" => "npm test",
|
|
173
|
+
"node" => "npm test"
|
|
174
|
+
}.freeze
|
|
175
|
+
|
|
176
|
+
class << self
|
|
177
|
+
def lsp_candidates(lang)
|
|
178
|
+
LSP_CANDIDATES[lang.to_s.downcase] || []
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def binary_available?(cmd, root_dir: nil)
|
|
182
|
+
return false if cmd.nil? || cmd.to_s.strip.empty?
|
|
183
|
+
|
|
184
|
+
bin = cmd.to_s.strip.split.first
|
|
185
|
+
if root_dir && bin && (bin.include?("/") || (Rubik::Platform.windows? && bin.include?("\\")))
|
|
186
|
+
full_path = File.expand_path(bin, root_dir)
|
|
187
|
+
return true if Rubik::Platform.executable_file?(full_path)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
!Rubik::Platform.which(cmd).nil?
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def probe_lsp(lang, dir = nil, root_dir: nil, custom_cmd: nil)
|
|
194
|
+
effective_root = root_dir || dir || Dir.pwd
|
|
195
|
+
lang_key = lang.to_s.downcase
|
|
196
|
+
candidates = []
|
|
197
|
+
candidates << custom_cmd.to_s.strip if custom_cmd && !custom_cmd.to_s.strip.empty?
|
|
198
|
+
candidates.concat(lsp_candidates(lang_key))
|
|
199
|
+
candidates.uniq!
|
|
200
|
+
|
|
201
|
+
candidates.find { |c| binary_available?(c, root_dir: effective_root) }
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def test_runner_candidates(lang, dir = nil, root_dir: nil, config: nil)
|
|
205
|
+
effective_root = root_dir || dir || Dir.pwd
|
|
206
|
+
lang_key = lang.to_s.downcase
|
|
207
|
+
marker_rules = TEST_RUNNER_MARKERS[lang_key] || []
|
|
208
|
+
matched_candidates = []
|
|
209
|
+
# Ruby-specific: a committed Gemfile.lock pins the gem stack, so any
|
|
210
|
+
# bare (non-bundler) fallback runner must be suppressed — otherwise
|
|
211
|
+
# a default-gem (e.g. minitest 6) silently diverges from the pinned
|
|
212
|
+
# stack. See _plan/test-stratifikasyonu-2026-09-14.md (Package C).
|
|
213
|
+
ruby_lock_pinned = lang_key == "ruby" &&
|
|
214
|
+
File.exist?(File.join(effective_root, "Gemfile.lock"))
|
|
215
|
+
|
|
216
|
+
marker_rules.each do |rule|
|
|
217
|
+
marker_path = File.join(effective_root, rule[:marker])
|
|
218
|
+
next unless File.exist?(marker_path)
|
|
219
|
+
|
|
220
|
+
candidates = rule[:candidates]
|
|
221
|
+
if lang_key == "ruby" && rule[:marker] == "Gemfile"
|
|
222
|
+
has_spec_dir = File.directory?(File.join(effective_root, "spec"))
|
|
223
|
+
has_lock = File.exist?(File.join(effective_root, "Gemfile.lock"))
|
|
224
|
+
# A committed lockfile means the operator pinned the gem stack;
|
|
225
|
+
# running bare `rake`/`ruby` would bypass it and can silently
|
|
226
|
+
# load a different (e.g. default-gem) version. With a lockfile
|
|
227
|
+
# only bundler-wrapped commands are offered; without one the
|
|
228
|
+
# historical candidate order is preserved.
|
|
229
|
+
candidates = if has_spec_dir
|
|
230
|
+
["bundle exec rspec", "bundle exec rake test", "rake test"]
|
|
231
|
+
elsif has_lock
|
|
232
|
+
["bundle exec rake test"]
|
|
233
|
+
else
|
|
234
|
+
["bundle exec rake test", "rake test", "bundle exec rspec"]
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
matched_candidates.concat(candidates)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Add default fallback (suppressed when a ruby lockfile pins bundler)
|
|
241
|
+
default_runner = DEFAULT_TEST_RUNNERS[lang_key]
|
|
242
|
+
matched_candidates << default_runner if default_runner && !(ruby_lock_pinned && !default_runner.start_with?("bundle exec"))
|
|
243
|
+
|
|
244
|
+
# Prepend explicit test command from config if configured
|
|
245
|
+
cfg_cmd = resolve_config_test_cmd(config, lang_key)
|
|
246
|
+
matched_candidates.unshift(cfg_cmd) if cfg_cmd && !cfg_cmd.empty?
|
|
247
|
+
|
|
248
|
+
matched_candidates.uniq
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def discover_test_runners(dir = nil, root_dir: nil, lang: nil, config: nil)
|
|
252
|
+
effective_root = root_dir || (dir.is_a?(String) ? dir : nil) || Dir.pwd
|
|
253
|
+
target_lang = lang ? lang.to_s.downcase : detect_primary_language(effective_root)
|
|
254
|
+
return [] unless target_lang
|
|
255
|
+
|
|
256
|
+
candidates = test_runner_candidates(target_lang, root_dir: effective_root, config: config)
|
|
257
|
+
candidates.select { |cmd| binary_available?(cmd, root_dir: effective_root) }
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def discover_formatters(dir = nil, root_dir: nil, lang: nil)
|
|
261
|
+
effective_root = root_dir || (dir.is_a?(String) ? dir : nil) || Dir.pwd
|
|
262
|
+
target_lang = lang ? lang.to_s.downcase : detect_primary_language(effective_root)
|
|
263
|
+
return [] unless target_lang
|
|
264
|
+
|
|
265
|
+
candidates = FORMATTER_CANDIDATES[target_lang] || []
|
|
266
|
+
candidates.select { |cmd| binary_available?(cmd, root_dir: effective_root) }
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def detect_primary_language(dir = nil, root_dir: nil)
|
|
270
|
+
effective_root = root_dir || dir || Dir.pwd
|
|
271
|
+
|
|
272
|
+
# Check root project marker files
|
|
273
|
+
return "ruby" if File.exist?(File.join(effective_root, "Gemfile")) || File.exist?(File.join(effective_root, "Rakefile"))
|
|
274
|
+
return "rust" if File.exist?(File.join(effective_root, "Cargo.toml"))
|
|
275
|
+
return "go" if File.exist?(File.join(effective_root, "go.mod"))
|
|
276
|
+
return "zig" if File.exist?(File.join(effective_root, "build.zig"))
|
|
277
|
+
return "python" if File.exist?(File.join(effective_root,
|
|
278
|
+
"pyproject.toml")) || File.exist?(File.join(effective_root,
|
|
279
|
+
"setup.py")) || File.exist?(File.join(effective_root,
|
|
280
|
+
"requirements.txt"))
|
|
281
|
+
return "typescript" if File.exist?(File.join(effective_root, "tsconfig.json"))
|
|
282
|
+
return "javascript" if File.exist?(File.join(effective_root, "package.json"))
|
|
283
|
+
return "c" if File.exist?(File.join(effective_root, "CMakeLists.txt"))
|
|
284
|
+
|
|
285
|
+
# Fallback: inspect extensions in directory
|
|
286
|
+
ext_counts = Hash.new(0)
|
|
287
|
+
begin
|
|
288
|
+
Dir.children(effective_root).each do |entry|
|
|
289
|
+
next if entry.start_with?(".")
|
|
290
|
+
|
|
291
|
+
full_p = File.join(effective_root, entry)
|
|
292
|
+
if File.file?(full_p)
|
|
293
|
+
ext = File.extname(entry).downcase
|
|
294
|
+
ext_counts[ext] += 1
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR
|
|
298
|
+
# ignore
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
ext_to_lang = {
|
|
302
|
+
".rb" => "ruby",
|
|
303
|
+
".rs" => "rust",
|
|
304
|
+
".go" => "go",
|
|
305
|
+
".py" => "python",
|
|
306
|
+
".zig" => "zig",
|
|
307
|
+
".ts" => "typescript",
|
|
308
|
+
".js" => "javascript",
|
|
309
|
+
".vue" => "vue",
|
|
310
|
+
".c" => "c",
|
|
311
|
+
".cpp" => "c"
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
best_ext = ext_counts.max_by { |_ext, count| count }&.first
|
|
315
|
+
ext_to_lang[best_ext] || "ruby"
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
private
|
|
319
|
+
|
|
320
|
+
def resolve_config_test_cmd(config, lang_key)
|
|
321
|
+
return nil unless config
|
|
322
|
+
|
|
323
|
+
cmd = if config.is_a?(Hash)
|
|
324
|
+
config["adapter.#{lang_key}.test_command"] ||
|
|
325
|
+
config["project.test_command"] ||
|
|
326
|
+
config.dig("adapter", lang_key, "test_command") ||
|
|
327
|
+
config.dig("adapter", lang_key.to_sym, "test_command") ||
|
|
328
|
+
config.dig("project", "test_command")
|
|
329
|
+
elsif config.respond_to?(:fetch)
|
|
330
|
+
begin
|
|
331
|
+
config.fetch("adapter.#{lang_key}.test_command", nil) ||
|
|
332
|
+
config.fetch("project.test_command", nil)
|
|
333
|
+
rescue KeyError, NoMethodError, TypeError
|
|
334
|
+
nil
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
cmd&.to_s&.strip
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "context"
|
|
4
|
+
|
|
5
|
+
module Rubik
|
|
6
|
+
module Runtime
|
|
7
|
+
# = Rubik::Runtime::ComponentFiber
|
|
8
|
+
#
|
|
9
|
+
# Component Lifecycle Unit for Spatiotemporal Composability (AGENTS.md §2).
|
|
10
|
+
#
|
|
11
|
+
# == Architecture & State Machine:
|
|
12
|
+
# A ComponentFiber represents the running instance of a pluggable runtime component.
|
|
13
|
+
# It strictly follows the Fiber Lifecycle State Machine defined in AGENTS.md:
|
|
14
|
+
#
|
|
15
|
+
# [Registered] -> PENDING <-------------------------+
|
|
16
|
+
# | |
|
|
17
|
+
# | Coeffects Satisfied | Coeffects Lost /
|
|
18
|
+
# v | Unload Requested
|
|
19
|
+
# ACTIVE --------------------------+
|
|
20
|
+
# |
|
|
21
|
+
# | Error during Activation
|
|
22
|
+
# v
|
|
23
|
+
# FAILED -> (Partial Inverses / Rollback) -> DISPOSED
|
|
24
|
+
#
|
|
25
|
+
# == Distinction from Ruby's ::Fiber:
|
|
26
|
+
# Rubik::Runtime::ComponentFiber is NOT a Ruby standard library coroutine (::Fiber) or
|
|
27
|
+
# lightweight cooperative thread. Instead, it is an architectural lifecycle fiber
|
|
28
|
+
# that encapsulates a component's coeffect dependencies (:inject), scoped sub-context
|
|
29
|
+
# (@sub_context = context.extend), and LIFO effect inverses (Omega accumulator)
|
|
30
|
+
# ensuring zero-restart reloading and atomic rollback.
|
|
31
|
+
#
|
|
32
|
+
# == Concurrency Contract (review 2026-09-17, Finding 2):
|
|
33
|
+
# ComponentFiber is intentionally lock-free: correctness relies on external
|
|
34
|
+
# serialization, NOT on internal locking. State mutations (@state,
|
|
35
|
+
# @sub_context, @instance) and the multi-step activate!/deactivate! sequences
|
|
36
|
+
# MUST happen under the owning Manager's Monitor (via register_component /
|
|
37
|
+
# provide / reconcile_all / swap_component) or in single-threaded code.
|
|
38
|
+
#
|
|
39
|
+
# Why no blanket per-fiber Mutex: the factory passed to activate! runs while
|
|
40
|
+
# the caller holds the Manager's Monitor, and factories may call back into
|
|
41
|
+
# the Manager (Monitor is reentrant). A fiber-local mutex would create a
|
|
42
|
+
# lock-order inversion (fiber lock -> Manager Monitor vs Manager Monitor ->
|
|
43
|
+
# fiber lock) and deadlock. The Manager's reentrant Monitor is the single
|
|
44
|
+
# serialization point; this class deliberately stays lock-free.
|
|
45
|
+
class ComponentFiber
|
|
46
|
+
STATES = [:unloaded, :pending, :active, :failed, :disposed].freeze
|
|
47
|
+
|
|
48
|
+
attr_reader :name, :inject, :requires, :provides, :config, :state, :last_error, :sub_context, :instance
|
|
49
|
+
|
|
50
|
+
def initialize(name:, inject: [], requires: [], provides: [], config: {}, &factory)
|
|
51
|
+
@name = name.to_s
|
|
52
|
+
@inject = Array(inject).map(&:to_s).map(&:downcase)
|
|
53
|
+
@requires = Array(requires).map(&:to_s).map(&:downcase)
|
|
54
|
+
@provides = ([@name.downcase] + Array(provides).map(&:to_s).map(&:downcase)).uniq
|
|
55
|
+
@config = config || {}
|
|
56
|
+
@factory = factory
|
|
57
|
+
@state = :pending
|
|
58
|
+
@last_error = nil
|
|
59
|
+
@sub_context = nil
|
|
60
|
+
@instance = nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def satisfiable?(context)
|
|
64
|
+
@inject.all? { |dep| context.has?(dep) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# A fiber is ready to activate when its inject dependencies are satisfied
|
|
68
|
+
# AND all its requires: coeffect prerequisites are also present in the context.
|
|
69
|
+
# `requires:` is a pre-activation gate (Plan 86 D-3): the fiber stays Pending
|
|
70
|
+
# until every declared requirement is available, preventing partial activation.
|
|
71
|
+
def ready_to_activate?(context)
|
|
72
|
+
satisfiable?(context) && @requires.all? { |dep| context.has?(dep) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def activate!(context)
|
|
76
|
+
return true if @state == :active
|
|
77
|
+
return false unless ready_to_activate?(context)
|
|
78
|
+
|
|
79
|
+
# Scoped sub-context for fiber lifecycle
|
|
80
|
+
@sub_context = context.extend
|
|
81
|
+
|
|
82
|
+
begin
|
|
83
|
+
@instance = if @factory
|
|
84
|
+
@factory.call(@sub_context, @config)
|
|
85
|
+
else
|
|
86
|
+
build_instance(@sub_context, @config)
|
|
87
|
+
end
|
|
88
|
+
@state = :active
|
|
89
|
+
@last_error = nil
|
|
90
|
+
true
|
|
91
|
+
rescue StandardError => e
|
|
92
|
+
# Atomic Rollback on Error: unwind partial inverses immediately
|
|
93
|
+
@sub_context&.dispose
|
|
94
|
+
@sub_context = nil
|
|
95
|
+
@instance = nil
|
|
96
|
+
@state = :failed
|
|
97
|
+
@last_error = e
|
|
98
|
+
false
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def deactivate!
|
|
103
|
+
return false unless @state == :active
|
|
104
|
+
|
|
105
|
+
if @sub_context
|
|
106
|
+
@sub_context.dispose
|
|
107
|
+
@sub_context = nil
|
|
108
|
+
end
|
|
109
|
+
@instance = nil
|
|
110
|
+
@state = :pending
|
|
111
|
+
true
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def reactivate!(context)
|
|
115
|
+
deactivate! if @state == :active
|
|
116
|
+
activate!(context)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def mark_failed!(error = nil)
|
|
120
|
+
deactivate! if @state == :active
|
|
121
|
+
@state = :failed
|
|
122
|
+
@last_error = error || StandardError.new("Fiber '#{@name}' marked failed")
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def dispose!
|
|
126
|
+
deactivate! if @state == :active
|
|
127
|
+
@state = :disposed
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def build_instance(_sub_context, _config)
|
|
131
|
+
nil
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Backwards-compatibility alias (AGENTS.md terminology). Kept for external
|
|
136
|
+
# consumers and pin test `test_component_fiber_naming_and_alias_compatibility`;
|
|
137
|
+
# internal code must use ComponentFiber directly — a bare `Fiber` inside
|
|
138
|
+
# Rubik::Runtime shadows ::Fiber and corrupts grep/grep-ability (review
|
|
139
|
+
# Finding 6). Enforced by test_no_bare_fiber_alias_use_in_lib.
|
|
140
|
+
Fiber = ComponentFiber
|
|
141
|
+
end
|
|
142
|
+
end
|