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,361 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module Orchestrator
|
|
5
|
+
module GoalLedger
|
|
6
|
+
# Atomic validate-then-apply. Op'lar sırayla, ÖNCEKİ op'ların
|
|
7
|
+
# uygulanmış olduğu working copy üzerinde valide edilir (RFC 6902
|
|
8
|
+
# semantiği); fail-fast YOK — tüm op'lar teşhis edilir ki tek retry
|
|
9
|
+
# her şeyi düzeltsin (bounded-retry bütçesi: DEFAULT_MAX_RETRIES;
|
|
10
|
+
# retry loop'unu feature katmanı yazar). Herhangi bir reason düşerse
|
|
11
|
+
# working copy ÇÖPE gider ve current_doc HİÇ dokunulmadan döner —
|
|
12
|
+
# §1.A "atomic failure rollback"in veri-düzlemi ikizi. Kabul edilirse
|
|
13
|
+
# rev tam olarak bir artar ve sonuç deep-frozen döner (doküman
|
|
14
|
+
# cap'lidir; validator'u dökümanı sınırsız büyütecek şekilde çağırmak
|
|
15
|
+
# sözleşme dışıdır).
|
|
16
|
+
module Validator
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
# patch_or_text: String (önce parse edilir) | GoalLedger::Patch
|
|
20
|
+
# -> [:ok, new_doc, new_rev] | [:rejected, [reason, ...]]
|
|
21
|
+
# Model girdisinde ASLA raise etmez (her başarısızlık veridir);
|
|
22
|
+
# yalnız programcı hatası ArgumentError'a düşer.
|
|
23
|
+
def validate(patch_or_text, schema:, current_doc:)
|
|
24
|
+
raise ArgumentError, "schema must be a GoalLedger::Schema" unless schema.is_a?(Schema)
|
|
25
|
+
|
|
26
|
+
verdict = patch_or_text.is_a?(Patch) ? [:ok, patch_or_text] : Patch.parse(patch_or_text)
|
|
27
|
+
return verdict unless verdict[0] == :ok
|
|
28
|
+
|
|
29
|
+
patch = verdict[1]
|
|
30
|
+
current_rev = current_doc.is_a?(Hash) ? current_doc["rev"] : nil
|
|
31
|
+
unless current_rev.is_a?(Integer)
|
|
32
|
+
return [:rejected, [rejection(:conflict, nil, "", "the current ledger has no integer rev",
|
|
33
|
+
"re-read the ledger — it may not be initialized yet")]]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# :conflict — küçük-model taksonomisinin %68'i (premature state
|
|
37
|
+
# overwrite) burada deterministik olarak durur; ESKİ doküman
|
|
38
|
+
# korunur, model güncel rev'e karşı yeniden yazmak zorundadır.
|
|
39
|
+
if patch.rev != current_rev
|
|
40
|
+
return [:rejected, [rejection(:conflict, nil, "",
|
|
41
|
+
"patch rev #{patch.rev} does not match ledger rev #{current_rev}",
|
|
42
|
+
"re-read the ledger (rev #{current_rev}) and re-emit the patch " \
|
|
43
|
+
"against it — your view is stale")]]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
working = deep_dup(current_doc)
|
|
47
|
+
reasons = []
|
|
48
|
+
patch.ops.each do |op|
|
|
49
|
+
# Patch.parse op değerlerini deep-frozen taşır (VO sözleşmesi);
|
|
50
|
+
# working copy'ye SICAK kopyası yerleşir — yoksa sonraki op'lar
|
|
51
|
+
# frozen Hash'lere yazmaya çalışır (sequential-op semantiği).
|
|
52
|
+
op = { **op, value: deep_dup(op[:value]) } if op[:value]
|
|
53
|
+
reasons.concat(apply_op(op, schema, working))
|
|
54
|
+
end
|
|
55
|
+
return [:rejected, reasons] unless reasons.empty?
|
|
56
|
+
|
|
57
|
+
working["rev"] = current_rev + 1
|
|
58
|
+
[:ok, deep_freeze(working), working["rev"]]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# ---- op dispatch ----
|
|
62
|
+
|
|
63
|
+
def apply_op(op, schema, doc)
|
|
64
|
+
head = op[:tokens][0]
|
|
65
|
+
section = schema.sections[head]
|
|
66
|
+
return section_op(op, head, section, doc) if section
|
|
67
|
+
|
|
68
|
+
field = schema.root_scalars[head]
|
|
69
|
+
return root_scalar_op(op, head, field, doc) if field
|
|
70
|
+
|
|
71
|
+
patchable = schema.root_scalars.keys.reject { |key| schema.root_scalars[key].managed }
|
|
72
|
+
[rejection(:schema, op[:index], op[:path], "unknown ledger section '#{head}'",
|
|
73
|
+
"known sections: #{schema.sections.keys.join(', ')}; root fields: #{patchable.join(', ')}")]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def root_scalar_op(op, name, spec, doc)
|
|
77
|
+
if spec.managed
|
|
78
|
+
return [rejection(:schema, op[:index], op[:path], "'#{name}' is runtime-managed",
|
|
79
|
+
"send the current rev in the patch header; the runtime writes it")]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
if op[:op] == :remove
|
|
83
|
+
return [rejection(:schema, op[:index], op[:path], "the '#{name}' field is required",
|
|
84
|
+
"replace it instead of removing it (#{op[:path]} with a new value)")]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
err = check_value(spec, op[:value], name, op)
|
|
88
|
+
return [err] if err
|
|
89
|
+
|
|
90
|
+
doc[name] = op[:value] # add == replace, RFC 6902 üye semantiği
|
|
91
|
+
[]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def section_op(op, name, section, doc)
|
|
95
|
+
coll = doc[name]
|
|
96
|
+
unless coll.is_a?(Array)
|
|
97
|
+
return [rejection(:schema, op[:index], op[:path], "section '#{name}' is not a list in the current ledger",
|
|
98
|
+
"the ledger may be corrupted — re-read it before patching")]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
return [section_head_rejection(op, name)] if op[:tokens].size == 1
|
|
102
|
+
|
|
103
|
+
return element_op(op, name, section, coll, op[:tokens][1]) if section.scalar
|
|
104
|
+
|
|
105
|
+
item_op(op, name, section.item, coll)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Derinlik 1 (bütünsel section yazımı) — üç fiil de yapısal red.
|
|
109
|
+
def section_head_rejection(op, name)
|
|
110
|
+
lead = case op[:op]
|
|
111
|
+
when :replace then "wholesale section rewrite is not allowed"
|
|
112
|
+
when :add then "cannot add the section '#{name}' itself"
|
|
113
|
+
else "sections are structural"
|
|
114
|
+
end
|
|
115
|
+
rejection(:schema, op[:index], op[:path], lead,
|
|
116
|
+
"address items individually (/#{name}/3, /#{name}/-) — one op per item")
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# ---- skalar liste (open_questions) ----
|
|
120
|
+
|
|
121
|
+
def element_op(op, name, section, coll, idx_tok)
|
|
122
|
+
if op[:tokens].size > 2
|
|
123
|
+
return [rejection(:schema, op[:index], op[:path],
|
|
124
|
+
"paths deeper than two levels are not supported in string lists like #{name}",
|
|
125
|
+
"address elements directly (/#{name}/0, /#{name}/-)")]
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
spec = FieldSpec.new(type: section.scalar, max_len: section.max_len)
|
|
129
|
+
if idx_tok == "-"
|
|
130
|
+
return [append_only_rejection(op, name)] unless op[:op] == :add
|
|
131
|
+
|
|
132
|
+
err = check_value(spec, op[:value], name, op)
|
|
133
|
+
return [err] if err
|
|
134
|
+
|
|
135
|
+
coll << op[:value]
|
|
136
|
+
return []
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
idx = index_token(idx_tok, name, coll, op, insertable: op[:op] == :add)
|
|
140
|
+
return [idx] unless idx.is_a?(Integer)
|
|
141
|
+
|
|
142
|
+
err = op[:op] == :remove ? nil : check_value(spec, op[:value], name, op)
|
|
143
|
+
return [err] if err
|
|
144
|
+
|
|
145
|
+
if op[:op] == :add
|
|
146
|
+
coll.insert(idx, op[:value])
|
|
147
|
+
elsif op[:op] == :replace
|
|
148
|
+
coll[idx] = op[:value]
|
|
149
|
+
else
|
|
150
|
+
coll.delete_at(idx)
|
|
151
|
+
end
|
|
152
|
+
[]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# ---- item listesi (tasks/findings/...) ----
|
|
156
|
+
|
|
157
|
+
def item_op(op, name, spec, coll)
|
|
158
|
+
return whole_item_op(op, name, spec, coll, op[:tokens][1]) if op[:tokens].size == 2
|
|
159
|
+
if op[:tokens].size > 3
|
|
160
|
+
return [rejection(:schema, op[:index], op[:path],
|
|
161
|
+
"paths deeper than three levels are not supported in item lists like #{name}",
|
|
162
|
+
"address items and their fields directly (e.g. /tasks/3/status)")]
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
field_op(op, name, spec, coll, op[:tokens][1])
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def whole_item_op(op, name, spec, coll, idx_tok)
|
|
169
|
+
if idx_tok == "-"
|
|
170
|
+
return [append_only_rejection(op, name)] unless op[:op] == :add
|
|
171
|
+
|
|
172
|
+
errs = validate_item_object(spec, op[:value], name, op)
|
|
173
|
+
return errs unless errs.empty?
|
|
174
|
+
|
|
175
|
+
coll << op[:value]
|
|
176
|
+
return dup_id_rejections(coll, spec, name, op)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
idx = index_token(idx_tok, name, coll, op, insertable: op[:op] == :add)
|
|
180
|
+
return [idx] unless idx.is_a?(Integer)
|
|
181
|
+
|
|
182
|
+
if op[:op] == :remove
|
|
183
|
+
coll.delete_at(idx)
|
|
184
|
+
return []
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
errs = validate_item_object(spec, op[:value], name, op)
|
|
188
|
+
return errs unless errs.empty?
|
|
189
|
+
|
|
190
|
+
op[:op] == :add ? coll.insert(idx, op[:value]) : coll[idx] = op[:value]
|
|
191
|
+
dup_id_rejections(coll, spec, name, op)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def field_op(op, name, spec, coll, idx_tok)
|
|
195
|
+
idx = index_token(idx_tok, name, coll, op, insertable: false)
|
|
196
|
+
return [idx] unless idx.is_a?(Integer)
|
|
197
|
+
|
|
198
|
+
field_name = op[:tokens][2]
|
|
199
|
+
field = spec.fields[field_name]
|
|
200
|
+
unless field
|
|
201
|
+
return [rejection(:schema, op[:index], op[:path], "unknown field '#{field_name}' on #{name} items",
|
|
202
|
+
"allowed fields: #{spec.fields.keys.join(', ')}")]
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
if op[:op] == :remove
|
|
206
|
+
nullable = spec.fields.select { |_, item_field| item_field.allow_null }.keys.join(", ")
|
|
207
|
+
return [rejection(:schema, op[:index], op[:path], "fields are not removable",
|
|
208
|
+
"replace with null where allowed (#{nullable}) or remove the item (/#{name}/#{idx})")]
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
err = check_value(field, op[:value], "#{name}.#{field_name}", op)
|
|
212
|
+
return [err] if err
|
|
213
|
+
|
|
214
|
+
coll[idx][field_name] = op[:value]
|
|
215
|
+
spec.id_field == field_name ? dup_id_rejections(coll, spec, name, op) : []
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# ---- değer kontrolü ----
|
|
219
|
+
|
|
220
|
+
def check_value(spec, value, where, op)
|
|
221
|
+
if value.nil? && !spec.allow_null
|
|
222
|
+
parent = op[:tokens].size >= 3 ? "/#{op[:tokens][0..-2].join('/')}" : op[:path]
|
|
223
|
+
return rejection(:schema, op[:index], op[:path], "#{where} does not accept null",
|
|
224
|
+
"null does not delete; use remove on the item (#{parent})")
|
|
225
|
+
end
|
|
226
|
+
return nil if value.nil?
|
|
227
|
+
|
|
228
|
+
type_err = check_type(spec, value, where, op)
|
|
229
|
+
return type_err if type_err
|
|
230
|
+
|
|
231
|
+
return nil unless spec.enum && !spec.enum.include?(value)
|
|
232
|
+
|
|
233
|
+
rejection(:schema, op[:index], op[:path],
|
|
234
|
+
"#{where} must be one of #{spec.enum.join(', ')} (got '#{value}')",
|
|
235
|
+
"pick from the allowed vocabulary")
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def check_type(spec, value, where, op)
|
|
239
|
+
case spec.type
|
|
240
|
+
when :string
|
|
241
|
+
unless value.is_a?(String)
|
|
242
|
+
return rejection(:schema, op[:index], op[:path],
|
|
243
|
+
"#{where}: expected string, got #{GoalLedger.json_type_name(value)}",
|
|
244
|
+
"use JSON types — strings are quoted, integers bare")
|
|
245
|
+
end
|
|
246
|
+
if spec.max_len && value.length > spec.max_len
|
|
247
|
+
return rejection(:schema, op[:index], op[:path],
|
|
248
|
+
"#{where} is #{value.length} chars; the cap is #{spec.max_len}",
|
|
249
|
+
"shorten the value — caps are deterministic (no stripping)")
|
|
250
|
+
end
|
|
251
|
+
when :integer
|
|
252
|
+
unless value.is_a?(Integer)
|
|
253
|
+
detail = value.is_a?(Float) ? "floats are not integers — send a whole number" : "expected integer"
|
|
254
|
+
return rejection(:schema, op[:index], op[:path],
|
|
255
|
+
"#{where}: #{detail}, got #{GoalLedger.json_type_name(value)}",
|
|
256
|
+
"integers are bare numbers without a decimal point")
|
|
257
|
+
end
|
|
258
|
+
when :boolean
|
|
259
|
+
unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
|
260
|
+
return rejection(:schema, op[:index], op[:path],
|
|
261
|
+
"#{where}: expected boolean, got #{GoalLedger.json_type_name(value)}",
|
|
262
|
+
"booleans are the bare literals true and false")
|
|
263
|
+
end
|
|
264
|
+
when :object
|
|
265
|
+
unless value.is_a?(Hash)
|
|
266
|
+
return rejection(:schema, op[:index], op[:path],
|
|
267
|
+
"#{where}: expected object, got #{GoalLedger.json_type_name(value)}",
|
|
268
|
+
"objects are brace-delimited key-value maps")
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
nil
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def validate_item_object(spec, value, name, op)
|
|
275
|
+
unless value.is_a?(Hash)
|
|
276
|
+
return [rejection(:schema, op[:index], op[:path],
|
|
277
|
+
"supply the full #{name} item as a JSON object (got #{GoalLedger.json_type_name(value)})",
|
|
278
|
+
"or address the field directly (#{op[:path]}/<field>, e.g. #{op[:path]}/status)")]
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
reasons = []
|
|
282
|
+
unknown = value.keys - spec.fields.keys
|
|
283
|
+
unless unknown.empty?
|
|
284
|
+
reasons << rejection(:schema, op[:index], op[:path],
|
|
285
|
+
"unknown field(s) #{unknown.join(', ')} on #{name} items",
|
|
286
|
+
"allowed fields: #{spec.fields.keys.join(', ')}")
|
|
287
|
+
end
|
|
288
|
+
missing = spec.fields.reject { |fname, item_field| item_field.optional || value.key?(fname) }.keys
|
|
289
|
+
unless missing.empty?
|
|
290
|
+
reasons << rejection(:schema, op[:index], op[:path],
|
|
291
|
+
"missing required field(s) #{missing.join(', ')}",
|
|
292
|
+
"supply the full item object or address the field directly (/#{name}/3/<field>)")
|
|
293
|
+
end
|
|
294
|
+
value.each do |fname, item_value|
|
|
295
|
+
field = spec.fields[fname]
|
|
296
|
+
next unless field
|
|
297
|
+
|
|
298
|
+
err = check_value(field, item_value, "#{name}.#{fname}", op)
|
|
299
|
+
reasons << err if err
|
|
300
|
+
end
|
|
301
|
+
reasons
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# ---- ortak yardımcılar ----
|
|
305
|
+
|
|
306
|
+
def index_token(tok, name, coll, op, insertable:)
|
|
307
|
+
unless tok.is_a?(String) && tok.match?(/\A\d+\z/)
|
|
308
|
+
return rejection(:schema, op[:index], op[:path], "expected an item index in #{name}, got '#{tok}'",
|
|
309
|
+
"numeric indices address existing items; '-' appends")
|
|
310
|
+
end
|
|
311
|
+
idx = tok.to_i
|
|
312
|
+
upper = insertable ? coll.size : coll.size - 1
|
|
313
|
+
return idx if idx <= upper
|
|
314
|
+
|
|
315
|
+
message = coll.empty? ? "#{name} is empty — nothing to address yet" : "#{name} has #{coll.size} item(s); valid indices 0-#{coll.size - 1}"
|
|
316
|
+
rejection(:schema, op[:index], op[:path], message, "append via /#{name}/-")
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def append_only_rejection(op, name)
|
|
320
|
+
rejection(:schema, op[:index], op[:path], "'-' appends; it cannot address an existing #{name} item",
|
|
321
|
+
"use a numeric index for replace/remove")
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def dup_id_rejections(coll, spec, name, op)
|
|
325
|
+
return [] unless spec.id_field
|
|
326
|
+
|
|
327
|
+
ids = coll.filter_map { |item| item.is_a?(Hash) ? item[spec.id_field] : nil }
|
|
328
|
+
dups = ids.tally.select { |_, count| count > 1 }.keys
|
|
329
|
+
return [] if dups.empty?
|
|
330
|
+
|
|
331
|
+
[rejection(:schema, op[:index], op[:path], "duplicate #{name} id '#{dups.first}'; ids must be unique within #{name}",
|
|
332
|
+
"pick a fresh id (e.g. a letter prefix plus the step number)")]
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def rejection(kind, op_index, path, message, hint)
|
|
336
|
+
GoalLedger.reason(kind, op_index: op_index, path: path, message: message, hint: hint)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def deep_dup(value)
|
|
340
|
+
case value
|
|
341
|
+
when Hash then value.to_a.to_h { |key, val| [key.dup, deep_dup(val)] }
|
|
342
|
+
when Array then value.map { |val| deep_dup(val) }
|
|
343
|
+
else value
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def deep_freeze(value)
|
|
348
|
+
case value
|
|
349
|
+
when Hash then value.each do |key, val|
|
|
350
|
+
deep_freeze(key)
|
|
351
|
+
deep_freeze(val)
|
|
352
|
+
end.freeze
|
|
353
|
+
when Array then value.each { |val| deep_freeze(val) }.freeze
|
|
354
|
+
when String then value.dup.freeze
|
|
355
|
+
else value
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "verify_spec"
|
|
4
|
+
|
|
5
|
+
module Rubik
|
|
6
|
+
module Orchestrator
|
|
7
|
+
module GoalLedger
|
|
8
|
+
# Tri-state FS doğrulaması (goal-ledger planı B). SAF değerlendirme:
|
|
9
|
+
# ledger'ın Monitor'lu SNAPSHOT'ı üzerinde çalışır, tüm FS IO kilit
|
|
10
|
+
# DIŞINDADır; flip kararı Drift/apply_internal_drift!'e aittir.
|
|
11
|
+
#
|
|
12
|
+
# Tri-state sözleşmesi (exhaustive honesty): ok = containment kanıtlı
|
|
13
|
+
# + predicate doğru; failed = containment kanıtlı + predicate yanlış
|
|
14
|
+
# (task → stale, env note → annotate); unknown = KANITLANAMAZ
|
|
15
|
+
# (workspace dışı, EACCES/EPERM, kesilmiş okumada needle yokluğu,
|
|
16
|
+
# malformed spec) — status flip YAPMAZ, yalnızca raporlanır.
|
|
17
|
+
module Verifier
|
|
18
|
+
module_function
|
|
19
|
+
|
|
20
|
+
# Adım sınırı girişi (loop.rb execute_steps hook'u): knob FIRST —
|
|
21
|
+
# kapalıysa hiç dokunma. goal_mode? guard'ı çağıran taraftadır.
|
|
22
|
+
def at_step_boundary!(loop_inst)
|
|
23
|
+
ledger = Rubik::Coeffects.get(loop_inst.runtime_manager&.context, Rubik::Coeffects::GOAL_LEDGER)
|
|
24
|
+
return nil unless ledger
|
|
25
|
+
|
|
26
|
+
verify!(ledger, trigger: :step, root_dir: loop_inst.buffer_manager&.root_dir || Dir.pwd,
|
|
27
|
+
config: loop_inst.config)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# -> nil (knob off / boş) | {rev:, flipped:, unknowns:, note:}
|
|
31
|
+
def verify!(ledger, trigger:, root_dir:, config: nil)
|
|
32
|
+
return nil unless Ledger.verify_enabled?(config)
|
|
33
|
+
|
|
34
|
+
snap = ledger.snapshot_doc
|
|
35
|
+
flips, unknowns = evaluate(snap, root_dir)
|
|
36
|
+
return nil if flips.empty? && unknowns.empty?
|
|
37
|
+
|
|
38
|
+
outcome = flips.empty? ? nil : ledger.apply_internal_drift!(flips)
|
|
39
|
+
Drift.emit(trigger: trigger, ledger: ledger, flips: flips, unknowns: unknowns, rev: outcome&.dig(:rev))
|
|
40
|
+
{ rev: outcome&.dig(:rev), flipped: flips.size, unknowns: unknowns,
|
|
41
|
+
note: flips.empty? ? nil : DriftNote.render(snap: ledger.snapshot_doc) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Kilit dışı: snapshot frozen Hash'lerdir; yalnız disk okunur.
|
|
45
|
+
def evaluate(snap, root_dir)
|
|
46
|
+
flips = []
|
|
47
|
+
unknowns = []
|
|
48
|
+
collect(snap, "tasks", root_dir, flips, unknowns)
|
|
49
|
+
collect(snap, "environment_notes", root_dir, flips, unknowns)
|
|
50
|
+
[flips, unknowns]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def collect(snap, collection, root_dir, flips, unknowns)
|
|
54
|
+
items = snap[collection]
|
|
55
|
+
return unless items.is_a?(Array)
|
|
56
|
+
|
|
57
|
+
if items.size > VerifySpec::MAX_SPECS_PER_COLLECTION * 4
|
|
58
|
+
# Flood guardı artık sessiz değil (review 2026-10-10): atlanan
|
|
59
|
+
# inceleme toplu unknown olarak görünür — asla sessiz spec düşme.
|
|
60
|
+
unknowns << { collection: collection, index: nil, id: "",
|
|
61
|
+
reason: "collection exceeds #{VerifySpec::MAX_SPECS_PER_COLLECTION * 4} items — re-verification skipped" }
|
|
62
|
+
return
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
items.each_with_index do |item, idx|
|
|
66
|
+
spec = item.is_a?(Hash) ? item["verify"] : nil
|
|
67
|
+
next if spec.nil?
|
|
68
|
+
|
|
69
|
+
verdict, detail = run_spec(spec, root_dir)
|
|
70
|
+
if verdict == :ok
|
|
71
|
+
# ok hiçbir şey yapmaz (sessiz) — stale temizliği yalnızca
|
|
72
|
+
# modelin normal ΔΣ patch'idir; harness asla iyileştirmez.
|
|
73
|
+
elsif verdict == :failed
|
|
74
|
+
flips << entry(collection, idx, item, "failed", detail)
|
|
75
|
+
else
|
|
76
|
+
unknowns << { collection: collection, index: idx, id: item_id(item), reason: detail }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# -> [:ok|:failed|:unknown, reason]
|
|
82
|
+
def run_spec(raw, root_dir)
|
|
83
|
+
verdict, spec_or_reason = VerifySpec.normalize(raw)
|
|
84
|
+
return [:unknown, spec_or_reason] unless verdict == :ok
|
|
85
|
+
|
|
86
|
+
expanded = File.expand_path(spec_or_reason[:path], root_dir)
|
|
87
|
+
return [:unknown, "path escapes the workspace (containment failed closed)"] unless Rubik::Platform.resolved_path_under?(expanded, root_dir)
|
|
88
|
+
|
|
89
|
+
if spec_or_reason[:type] == "file_exists"
|
|
90
|
+
File.exist?(expanded) ? [:ok, "file_exists"] : [:failed, "file_exists #{spec_or_reason[:path]}: missing"]
|
|
91
|
+
else
|
|
92
|
+
needle_read(spec_or_reason, expanded)
|
|
93
|
+
end
|
|
94
|
+
rescue Errno::EACCES, Errno::EPERM => e
|
|
95
|
+
[:unknown, "#{e.class.name.delete_prefix('Errno::')}: not readable"]
|
|
96
|
+
rescue StandardError => e
|
|
97
|
+
[:unknown, "#{e.class}: #{e.message}"]
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Kesilmiş okumada needle yokluğu KANITLANAMAZ → unknown (tri-state
|
|
101
|
+
# dürüstlüğünün kalbi); tam okumada yokluk gerçek yokluktur → failed.
|
|
102
|
+
def needle_read(spec, expanded)
|
|
103
|
+
size = File.size(expanded)
|
|
104
|
+
return [:failed, "file_contains #{spec[:path]}: path is a directory"] if File.directory?(expanded)
|
|
105
|
+
|
|
106
|
+
body = File.open(expanded, "rb") { |f| f.read(VerifySpec::READ_CAP_BYTES) }.to_s
|
|
107
|
+
body = Rubik::Platform.safe_utf8(body)
|
|
108
|
+
return [:ok, "file_contains"] if body.include?(spec[:needle])
|
|
109
|
+
return [:unknown, "read truncated at #{VerifySpec::READ_CAP_BYTES} bytes — absence unprovable"] if size > body.bytesize
|
|
110
|
+
|
|
111
|
+
[:failed, "file_contains #{spec[:path]}: needle absent"]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def stale?(item)
|
|
115
|
+
item["status"] == "stale"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def entry(collection, idx, item, state, reason)
|
|
119
|
+
{ collection: collection, index: idx, id: item_id(item), state: state, reason: reason,
|
|
120
|
+
from: item["status"], at: Time.now.utc.iso8601 }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def item_id(item)
|
|
124
|
+
item.is_a?(Hash) ? item["id"].to_s : ""
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module Orchestrator
|
|
5
|
+
module GoalLedger
|
|
6
|
+
# Verify-spec grammar SSOT'u (goal-ledger planı B): tasks[] ve
|
|
7
|
+
# environment_notes[] girdilerinin opsiyonel `verify` alanının kapalı
|
|
8
|
+
# tip seti. SAF — FS yok; lexical ön kontroller burada, containment
|
|
9
|
+
# kanıtı Verifier'dadır. "marker", file_contains'in okunabilir
|
|
10
|
+
# alias'ıdır (aynı semantiğin sentinel dili) — implementasyon tek
|
|
11
|
+
# predicate'e iner. Sınırlar sabiteedir (knoba knob eklemeyiz).
|
|
12
|
+
module VerifySpec
|
|
13
|
+
TYPES = ["file_exists", "file_contains", "marker"].freeze
|
|
14
|
+
NEEDLE_FIELD = { "file_contains" => "pattern", "marker" => "marker" }.freeze
|
|
15
|
+
MAX_SPECS_PER_COLLECTION = 16
|
|
16
|
+
PATH_MAX_CHARS = 512
|
|
17
|
+
NEEDLE_MAX_CHARS = 256
|
|
18
|
+
READ_CAP_BYTES = 262_144 # 256 KiB — kesilmiş okumada yokluk KANITLANAMAZ (unknown)
|
|
19
|
+
|
|
20
|
+
module_function
|
|
21
|
+
|
|
22
|
+
# -> [:ok, {type:, path:, needle:}] | [:invalid, neden]
|
|
23
|
+
# Hash girdi; tip kapalı sette; path workspace-relative (absolute
|
|
24
|
+
# ve ".." segmenti reddedilir — lexical ön kontrol, containment
|
|
25
|
+
# kanıtı Verifier'ın işi); uzunluk cap'leri.
|
|
26
|
+
def normalize(raw)
|
|
27
|
+
return [:invalid, "verify must be a JSON object"] unless raw.is_a?(Hash)
|
|
28
|
+
|
|
29
|
+
type = raw["type"]
|
|
30
|
+
return [:invalid, "unknown verify type #{GoalLedger.json_type_name(type)}"] unless TYPES.include?(type)
|
|
31
|
+
|
|
32
|
+
path = raw["path"]
|
|
33
|
+
return [:invalid, "verify path is required"] unless path.is_a?(String) && !path.empty?
|
|
34
|
+
return [:invalid, "verify path exceeds #{PATH_MAX_CHARS} chars"] if path.length > PATH_MAX_CHARS
|
|
35
|
+
return [:invalid, "verify path must be workspace-relative"] if path.start_with?("/", "\\\\")
|
|
36
|
+
return [:invalid, "verify path must not escape the workspace"] if path.split(%r{[\\/]}).include?("..")
|
|
37
|
+
|
|
38
|
+
needle_field = NEEDLE_FIELD[type]
|
|
39
|
+
if needle_field
|
|
40
|
+
needle = raw[needle_field]
|
|
41
|
+
return [:invalid, "verify #{type} requires '#{needle_field}'"] unless needle.is_a?(String) && !needle.empty?
|
|
42
|
+
return [:invalid, "verify #{needle_field} exceeds #{NEEDLE_MAX_CHARS} chars"] if needle.length > NEEDLE_MAX_CHARS
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
[:ok, { type: type, path: path, needle: needle_field ? raw[needle_field] : nil }.freeze]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../platform"
|
|
4
|
+
|
|
5
|
+
module Rubik
|
|
6
|
+
module Orchestrator
|
|
7
|
+
# GoalLedger — SKILL.state (arxiv 2608.26263v2) türevi yapılandırılmış
|
|
8
|
+
# goal durumu (Σ) için patch seam'i. SAF KÜTÜPHANE: I/O yok, thread yok,
|
|
9
|
+
# config yok, prompt/tool yüzeyi yok — bağlayan katman (goal-ledger
|
|
10
|
+
# feature planı) erişimi sahiplenir; bu dosya require zincirinde
|
|
11
|
+
# olmadığından yüklenmeyen kütüphane = davranış farkı yapısal olarak sıfır.
|
|
12
|
+
#
|
|
13
|
+
# GoalState'in aksine immutable value object'lerdir: Monitor'suzdur çünkü
|
|
14
|
+
# paylaşılan mutable durum yoktur; Validator atomic'tir (working-copy-
|
|
15
|
+
# then-discard) ve kabul edilen doküman deep-frozen çıkar.
|
|
16
|
+
module GoalLedger
|
|
17
|
+
# Ret reason'ının sözleşme şekli (beş anahtar HER ZAMAN mevcuttur —
|
|
18
|
+
# tüketici nil-dalı yazmaz); message/hint model-okunur İngilizce'dir ve
|
|
19
|
+
# retry feedback'inde birebir yankılanır. Hash bilinçli tercihtir:
|
|
20
|
+
# `class` üye adı Struct/Data'da yasaklıdır.
|
|
21
|
+
def reason(kind, op_index:, path:, message:, hint:)
|
|
22
|
+
{ class: kind, op_index: op_index, path: path, message: message, hint: hint }.freeze
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# JSON tip adları (reason mesajları modelin gösterdiği değeri JSON
|
|
26
|
+
# dünyasında adlandırır: "expected integer, got float").
|
|
27
|
+
def json_type_name(value)
|
|
28
|
+
case value
|
|
29
|
+
when nil then "null"
|
|
30
|
+
when true, false then "boolean"
|
|
31
|
+
when Integer then "integer"
|
|
32
|
+
when Float then "float"
|
|
33
|
+
when String then "string"
|
|
34
|
+
when Array then "array"
|
|
35
|
+
when Hash then "object"
|
|
36
|
+
else value.class.name
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Patch body flood guardı: gerçekçi dolu bir ledger ~2-6 KB; 32 KB
|
|
41
|
+
# ~5x headroom ama dosya-dump flood'unu keser (context_limit 32_768
|
|
42
|
+
# defaultuyla sayısal simetri — loop.rb).
|
|
43
|
+
MAX_PATCH_BYTES = 32_768
|
|
44
|
+
# Tek patch'te op tavanı: gerçek bir adım 3-8 op churn'lar; 64 ~10x
|
|
45
|
+
# headroom, yine de O(ops×doc) validasyon ve retry-echo boyutunu sınırlar.
|
|
46
|
+
MAX_OPS = 64
|
|
47
|
+
# Bounded-retry default'u: ret reason'ları tool-result feedback olarak
|
|
48
|
+
# yankılanır; bütçe tükenirse ESKİ Σ korunur (dmesg warn; goal loop
|
|
49
|
+
# asla brick olmaz). Config knob'u (goal.ledger.max_patch_retries)
|
|
50
|
+
# feature planına aittir.
|
|
51
|
+
DEFAULT_MAX_RETRIES = 2
|
|
52
|
+
|
|
53
|
+
module_function :reason, :json_type_name
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
require_relative "goal_ledger/patch"
|
|
59
|
+
require_relative "goal_ledger/schema"
|
|
60
|
+
require_relative "goal_ledger/validator"
|
|
61
|
+
require_relative "goal_ledger/ledger"
|
|
62
|
+
require_relative "goal_ledger/verify_spec"
|
|
63
|
+
require_relative "goal_ledger/verifier"
|
|
64
|
+
require_relative "goal_ledger/drift"
|
|
65
|
+
require_relative "goal_ledger/observation_projection"
|