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,250 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rubik
|
|
4
|
+
module Storage
|
|
5
|
+
module Vakanuvis
|
|
6
|
+
# Düzlem 2'nin durumlu yarısı: digest iş kuyruğu, doğum sicili ve
|
|
7
|
+
# kirli işaret — DistillGate kurallarının (doğrulayıcı, brif,
|
|
8
|
+
# makbuz) biriktirici Ω'su. ctx servisi DEĞİLDİR, kendi thread'i
|
|
9
|
+
# YOKTUR (worker Harness'te kalır); ömrü sahibi Harness'in ömrüdür
|
|
10
|
+
# (kompozisyon) — o yüzden ctx.on_dispose tersi gerekmez.
|
|
11
|
+
#
|
|
12
|
+
# Tek kilit (@lock) kuyruk + sicil + kirli işareti SAHİPLENİR ve
|
|
13
|
+
# kilit sıralaması kendiliğinden tekyönlüdür: @lock altında yalnız
|
|
14
|
+
# store/supervisor çağrılır (store monitoru asla geri çağırmadığı
|
|
15
|
+
# için @lock → store TEK yuvalanmadır); Harness'in @monitor'u @lock
|
|
16
|
+
# altında ASLA alınmaz. Eski with_gate → @monitor dansı silindi,
|
|
17
|
+
# taşınmadı — gate durumu ile harness durumu artık aynı kod yolunu
|
|
18
|
+
# paylaşmaz.
|
|
19
|
+
module DistillGate
|
|
20
|
+
# Doğum kapısı + iş defteri. Kurucu enjeksiyonuyla çalışır: store,
|
|
21
|
+
# supervisor, event_bus ve sözlü girdiler dışarıdan verilir —
|
|
22
|
+
# Harness'e ASLA geri dokunmaz.
|
|
23
|
+
class Gate
|
|
24
|
+
attr_reader :store, :supervisor, :dmesg, :session_slug, :max_steps, :model
|
|
25
|
+
|
|
26
|
+
def initialize(store:, max_steps:, session_slug:, supervisor: nil, dmesg: nil,
|
|
27
|
+
event_bus: nil, enabled: true, model: nil)
|
|
28
|
+
@store = store
|
|
29
|
+
@supervisor = supervisor
|
|
30
|
+
@dmesg = dmesg
|
|
31
|
+
@event_bus = event_bus
|
|
32
|
+
@enabled = enabled
|
|
33
|
+
@max_steps = max_steps
|
|
34
|
+
@model = model
|
|
35
|
+
@session_slug = session_slug
|
|
36
|
+
@pending_digests = []
|
|
37
|
+
@distillers = {}
|
|
38
|
+
@lock = Mutex.new
|
|
39
|
+
# v2 §4-V5: kirli işareti — son başarılı derlemeden bu yana KALICI
|
|
40
|
+
# içerik (memory+wiki+note satır ayak izi) değişmediyse toplu
|
|
41
|
+
# damıtıcı doğmaz. Turn satırları ayak izini değiştirmez: saf
|
|
42
|
+
# turn akışında derleme boşa yakılmaz.
|
|
43
|
+
@compile_footprint = nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# ---- Seam 1: maestro tool thread'i (Harness#digest delege eder) --
|
|
47
|
+
|
|
48
|
+
# Hedef için dedupe denetimi + itme TEK @lock kritik bölümünde;
|
|
49
|
+
# hemen ardından doğum denemesi AYRI bir @lock penceresinde
|
|
50
|
+
# (iki fazlı — tek büyük kritik bölüm İÇİNE ALMA). Doğum
|
|
51
|
+
# başarısızsa iş kuyrukta kalır: yarım durum birikmez.
|
|
52
|
+
# Dönen makbuz dizgesi bayt-bayt eski Harness#digest çıktısıdır.
|
|
53
|
+
def submit(query)
|
|
54
|
+
target = Vocabulary.wiki_ref(query)
|
|
55
|
+
# v2 §4-V5: aynı hedefli iş zaten kuyrukta ya da uçuştaysa makbuz
|
|
56
|
+
# 'already queued' varyantına düşer — çift doğum yok.
|
|
57
|
+
in_flight = @lock.synchronize do
|
|
58
|
+
busy = @pending_digests.any? { |j| j[:target] == target } ||
|
|
59
|
+
@distillers.any? { |_id, j| j[:target] == target }
|
|
60
|
+
unless busy
|
|
61
|
+
@pending_digests << { query: query, target: target,
|
|
62
|
+
transcript_uris: source_transcript_uris }
|
|
63
|
+
end
|
|
64
|
+
busy
|
|
65
|
+
end
|
|
66
|
+
if in_flight
|
|
67
|
+
return "Digest already queued for '#{target}' (#{AnnalWriter.one_line(query, 60)}): the chronicler is " \
|
|
68
|
+
"compiling it — recall it later with vakanuvis(action: 'recall', query: '...')."
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
spawned = spawn_job!(batch: false)
|
|
72
|
+
DistillGate.receipt(query: query, target: target, spawned: !spawned.nil?)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# ---- Seam 2: worker thread'i (Harness#handle_subagent çağırır) ---
|
|
76
|
+
|
|
77
|
+
# Ham subagent tamamlanma olayı. İş sicilden @lock altında
|
|
78
|
+
# silinir, KİLİT BIRAKILIR, sonra supervisor.get + hasat TEK
|
|
79
|
+
# kurtarma bölgesinde koşar: get/inst.result (hedeflenmemiş
|
|
80
|
+
# double, kayıp kayıt) ve DistillGate.harvest (disk hatası, bozuk
|
|
81
|
+
# satır) raise ederse iş kaybolmaz — hedefli iş kuyruğa geri
|
|
82
|
+
# konur (review: sessiz-yutma sınıfı), toplu iş dürüstçe düşer.
|
|
83
|
+
# Dönen değer: :compiled | :incomplete | :crashed | :rejected | nil.
|
|
84
|
+
def completion_event(payload)
|
|
85
|
+
job = @lock.synchronize { @distillers.delete(payload[:id].to_s) }
|
|
86
|
+
return unless job
|
|
87
|
+
|
|
88
|
+
outcome = begin
|
|
89
|
+
inst = @supervisor.respond_to?(:get) ? @supervisor.get(payload[:id].to_s) : nil
|
|
90
|
+
finish_body = inst.respond_to?(:result) ? inst.result : nil
|
|
91
|
+
if payload[:state].to_s != "completed" || finish_body.nil?
|
|
92
|
+
:incomplete
|
|
93
|
+
else
|
|
94
|
+
DistillGate.harvest(self, job, finish_body)
|
|
95
|
+
end
|
|
96
|
+
rescue StandardError => e
|
|
97
|
+
if job[:target]
|
|
98
|
+
@lock.synchronize { @pending_digests << job }
|
|
99
|
+
Vakanuvis.log_via(@dmesg, :warn,
|
|
100
|
+
"harvest crashed for #{job[:target]} (#{e.class}: #{e.message}) — digest requeued")
|
|
101
|
+
else
|
|
102
|
+
Vakanuvis.log_via(@dmesg, :warn,
|
|
103
|
+
"harvest crashed for batch compile (#{e.class}: #{e.message}) — compile dropped")
|
|
104
|
+
end
|
|
105
|
+
:crashed
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
case outcome
|
|
109
|
+
when :compiled
|
|
110
|
+
Vakanuvis.log_via(@dmesg, :info, "distiller compiled #{job[:target] || 'a wiki page'}")
|
|
111
|
+
when :incomplete
|
|
112
|
+
if job[:target]
|
|
113
|
+
@lock.synchronize { @pending_digests << job }
|
|
114
|
+
Vakanuvis.log_via(@dmesg, :warn,
|
|
115
|
+
"distiller for #{job[:target]} ended state=#{payload[:state]} — digest requeued")
|
|
116
|
+
else
|
|
117
|
+
Vakanuvis.log_via(@dmesg, :warn, "batch distiller ended state=#{payload[:state]} — compile dropped")
|
|
118
|
+
end
|
|
119
|
+
when :crashed, :rejected then nil # kendi dalında detay loglandı (crash requeue/drop; B6 ret sebebi + trim)
|
|
120
|
+
else
|
|
121
|
+
# Beklenmedik outcome — yanlış "schema ret" etiketi yerine
|
|
122
|
+
# gerçek değeri yaz (future harvest sembolleri sessiz kalmasın).
|
|
123
|
+
Vakanuvis.log_via(@dmesg, :warn, "distiller ended with unexpected outcome=#{outcome.inspect} — dropped, store untouched")
|
|
124
|
+
end
|
|
125
|
+
outcome
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# ---- Seam 3: worker thread'inin flush noktası (Harness#maintain) --
|
|
129
|
+
|
|
130
|
+
# Toplu doğum kararı, kirli işarete bağlıdır (v2 §4-V5): kalıcı
|
|
131
|
+
# içerik değişmediyse damıtıcı yakılmaz.
|
|
132
|
+
def flush_tick
|
|
133
|
+
spawn_job!(batch: true) if dirty?
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Salt-okunuma peeki (test/kurcalama yüzeyi): kuyruğun başını
|
|
137
|
+
# gösterir — tüketmez. Üretim görünürlüğü aşağıdaki job_targets'tır.
|
|
138
|
+
def peek_job(batch: false)
|
|
139
|
+
@lock.synchronize { @pending_digests.first || (batch ? { batch: true } : nil) }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# İş defteri görünümü (issue #100 P2b): kuyruktaki + uçuştaki
|
|
143
|
+
# hedefler. @lock altında YALNIZ kuyruk/sicil okunur — store'a
|
|
144
|
+
# dokunulmaz (kilit sırası tekyönlü kalır).
|
|
145
|
+
def job_targets
|
|
146
|
+
@lock.synchronize do
|
|
147
|
+
{ queued: @pending_digests.map { |j| j[:target] || "batch compile" },
|
|
148
|
+
in_flight: @distillers.values.map { |j| j[:target] || "batch compile" } }
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Başarılı derlemenin hemen ardından DistillGate çağırır — ayak izi
|
|
153
|
+
# o anki hale snapshot'lanır; sonraki flush ancak YENİ kalıcı
|
|
154
|
+
# içerikte doğum kararı verir. Snapshot'ın kendisi patlarsa sayfa
|
|
155
|
+
# zaten başarılı yazılmıştır: gate kirli kalır (bir sonraki
|
|
156
|
+
# flush'ta yeniden snapshot denenir) — ekonomi kaybı, doğruluk değil.
|
|
157
|
+
def mark_compiled!
|
|
158
|
+
@lock.synchronize { @compile_footprint = footprint }
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Oturum annal'ının rubik:// URI'si (DistillGate.brief kaynak
|
|
162
|
+
# listesi için okur). Annal yoksa nil — brif o kaynağı listelemez.
|
|
163
|
+
def session_annal_uri
|
|
164
|
+
rec = @store.get(type: "note", id: Vocabulary.session_ref(@session_slug))
|
|
165
|
+
rec && @store.uri_for("note", rec["id"])
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
private
|
|
169
|
+
|
|
170
|
+
# Doğum kararı: digest kuyruğu önceliklidir. İş claim→spawn→confirm
|
|
171
|
+
# dizisiyle TEK kapıdan (@lock) alınır: maestro thread'i (submit)
|
|
172
|
+
# ile worker'ın flush'u aynı iş için çift doğum yarışamaz; doğum
|
|
173
|
+
# başarısızsa iş kuyruğa geri konur, sonraki flush'ta yeniden
|
|
174
|
+
# denenir.
|
|
175
|
+
def spawn_job!(batch:)
|
|
176
|
+
return unless spawn_allowed?
|
|
177
|
+
|
|
178
|
+
@lock.synchronize do
|
|
179
|
+
job = claim_job(batch: batch)
|
|
180
|
+
next unless job
|
|
181
|
+
|
|
182
|
+
begin
|
|
183
|
+
inst = @supervisor.spawn(
|
|
184
|
+
role: DISTILLER_ROLE, prompt: DistillGate.brief(self, job), type_name: TYPE_NAME,
|
|
185
|
+
# parent_id omitted — the root plane is spawn's default
|
|
186
|
+
# (ConversationId::ROOT_ID); storage stays plane-blind.
|
|
187
|
+
max_steps: @max_steps, model: @model || "inherit"
|
|
188
|
+
)
|
|
189
|
+
confirm(inst.id, job)
|
|
190
|
+
Vakanuvis.log_via(@dmesg, :info, "distiller spawned for #{DistillGate.job_description(job)}")
|
|
191
|
+
inst
|
|
192
|
+
rescue StandardError => e
|
|
193
|
+
restore_job(job)
|
|
194
|
+
Vakanuvis.log_via(@dmesg, :warn,
|
|
195
|
+
"distiller spawn failed (#{DistillGate.job_description(job)}): #{e.class}: #{e.message}")
|
|
196
|
+
nil
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Bus yoksa hasat da olamaz (damıtıcının tamamlanma olayı gelmez):
|
|
202
|
+
# doğum kapısı kapalı kalır, makbuz dürüst kalır.
|
|
203
|
+
def spawn_allowed?
|
|
204
|
+
@enabled && @event_bus.respond_to?(:subscribe) &&
|
|
205
|
+
@supervisor.respond_to?(:spawn) &&
|
|
206
|
+
@supervisor.respond_to?(:type_registry) && @supervisor.type_registry.get(TYPE_NAME)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Aşağısı YALNIZ @lock altından çağrılır (çağıran: submit /
|
|
210
|
+
# spawn_job! / completion_event) — kendi kilidi almaz.
|
|
211
|
+
def claim_job(batch: false)
|
|
212
|
+
@pending_digests.shift || (batch ? { batch: true, transcript_uris: source_transcript_uris } : nil)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def restore_job(job)
|
|
216
|
+
@pending_digests.unshift(job) if job[:target]
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def confirm(inst_id, job)
|
|
220
|
+
@distillers[inst_id.to_s] = job
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def dirty?
|
|
224
|
+
@lock.synchronize { @compile_footprint.nil? || @compile_footprint != footprint }
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Kalıcı içerik ayak izi: kart + sayfa + not (annal dahil) satır
|
|
228
|
+
# sayıları. Turn satırları mevcut annal notuna satır ekler, ayak
|
|
229
|
+
# izini değiştirmez — saf turn akışında derleme boşa yakılmaz.
|
|
230
|
+
def footprint
|
|
231
|
+
["memory", "wiki", "note"].sum { |t| @store.list(type: t).size }
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Kaynak disiplini (v2 §4-V4): yalnız status=completed transcript
|
|
235
|
+
# satırları; kronikçinin kendi etiketlileri hariç — özyinelemeli
|
|
236
|
+
# kaynak kirliliği. En yeni N satır (updated_at, sonra row) —
|
|
237
|
+
# deterministik sıra.
|
|
238
|
+
def source_transcript_uris
|
|
239
|
+
summaries = @store.list(type: "transcript").reject do |s|
|
|
240
|
+
s.status != Vocabulary::TRANSCRIPT_COMPLETED || Array(s.tags).include?(Vocabulary::BASE_TAG)
|
|
241
|
+
end
|
|
242
|
+
summaries.sort_by { |s| [s.updated_at.to_s, s.row.to_i] }
|
|
243
|
+
.last(Harness::SOURCE_TRANSCRIPT_LIMIT)
|
|
244
|
+
.map { |s| @store.uri_for("transcript", s.id) }
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "monitor"
|
|
4
|
+
|
|
5
|
+
module Rubik
|
|
6
|
+
module Storage
|
|
7
|
+
module Vakanuvis
|
|
8
|
+
# Düzlem 1 — Deterministik Çekirdek: tek worker thread + sınırlı
|
|
9
|
+
# FIFO kuyruk. Olay kapıları mevcut bus abonelikleridir (subagent
|
|
10
|
+
# tamamlanma + goal bitişi — K3); NE ZAMAN, NEREYE, NASIL yazılacağı
|
|
11
|
+
# burada kural tabanlıdır. Sessizlik sözleşmesi (§4.3): WAKE_KINDS
|
|
12
|
+
# dışında kalır, notice yaymaz — sadece dmesg/stderr. Ömür: ctx
|
|
13
|
+
# dispose'u LIFO ile kapatır (worker kill + abonelik geri alımı).
|
|
14
|
+
class Harness
|
|
15
|
+
STOP = :__vakanuvis_stop__
|
|
16
|
+
MAX_QUEUE = 200
|
|
17
|
+
RECALL_LIMIT = 5
|
|
18
|
+
RECALL_FALLBACK_LIMIT = 3
|
|
19
|
+
RECALL_BODY_SLICE = 140
|
|
20
|
+
RECALL_TYPES = ["memory", "wiki"].freeze
|
|
21
|
+
# v2 §4-V4: damıtıcı kaynak disiplini — yalnız TAMAMLANMIŞ
|
|
22
|
+
# transcript'ler, damıtıcının kendisi Hariç (özyineleme kirliliği:
|
|
23
|
+
# canlı depoda transcript satırları 'vakanuvis' etiketi taşır).
|
|
24
|
+
SOURCE_TRANSCRIPT_LIMIT = 8
|
|
25
|
+
# Literaller, depolama katmanını orchestrator require'larından
|
|
26
|
+
# uzak tutar; test ikizleri her ikisini de sabitlere pinler:
|
|
27
|
+
# Notices::COMPLETED_EVENT ve GoalState::FINISH_EVENT. TURN_EVENT
|
|
28
|
+
# REPL TurnRecorder#emit_turn_finished kapısıdır (root-turn planı
|
|
29
|
+
# 2026-10-04).
|
|
30
|
+
SUBAGENT_EVENT = :subagent_completed
|
|
31
|
+
GOAL_EVENT = :goal_finished
|
|
32
|
+
TURN_EVENT = :turn_finished
|
|
33
|
+
|
|
34
|
+
attr_reader :store, :supervisor, :dmesg, :session_slug, :gate
|
|
35
|
+
|
|
36
|
+
def initialize(store:, config: nil, event_bus: nil, supervisor: nil, dmesg: nil,
|
|
37
|
+
context: nil, session_slug: nil, max_queue: MAX_QUEUE)
|
|
38
|
+
@store = store
|
|
39
|
+
@config = config
|
|
40
|
+
@event_bus = event_bus
|
|
41
|
+
@supervisor = supervisor
|
|
42
|
+
@dmesg = dmesg
|
|
43
|
+
@context = context
|
|
44
|
+
@session_slug = session_slug || self.class.mint_session_slug
|
|
45
|
+
@flush_events = fetch_cfg("subagents.vakanuvis.flush_events", 10).to_i
|
|
46
|
+
@max_steps = fetch_cfg("subagents.vakanuvis.max_steps", 8).to_i
|
|
47
|
+
@model = fetch_cfg("subagents.vakanuvis.model", nil)
|
|
48
|
+
@retention = fetch_cfg("subagents.vakanuvis.retention", "supersede").to_s
|
|
49
|
+
@distiller_enabled = fetch_cfg("subagents.vakanuvis.distiller", true) != false
|
|
50
|
+
@root_turns = fetch_cfg("subagents.vakanuvis.root_turns", true) != false
|
|
51
|
+
@max_queue = max_queue.to_i
|
|
52
|
+
@queue = Thread::Queue.new
|
|
53
|
+
@monitor = Monitor.new
|
|
54
|
+
@seq = 0
|
|
55
|
+
@goal_ordinal = 0
|
|
56
|
+
@turn_ordinal = 0
|
|
57
|
+
@events_since_distill = 0
|
|
58
|
+
@dropped = 0
|
|
59
|
+
@stopped = false
|
|
60
|
+
@busy = 0
|
|
61
|
+
@worker = nil
|
|
62
|
+
# Damıtıcı doğum kapısı: kuyruk + sicil + kirli işaret Gate'de
|
|
63
|
+
# (kurucu enjeksiyonu — gate Harness'e geri dokunmaz).
|
|
64
|
+
@gate = DistillGate::Gate.new(store: @store, supervisor: @supervisor, dmesg: @dmesg,
|
|
65
|
+
event_bus: @event_bus, enabled: @distiller_enabled,
|
|
66
|
+
max_steps: @max_steps, model: @model, session_slug: @session_slug)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class << self
|
|
70
|
+
# Oturum slug'ı: tek süreçte deterministik, süreçler arasında
|
|
71
|
+
# ayrışır. Replay testleri sabit slug enjekte eder.
|
|
72
|
+
def mint_session_slug
|
|
73
|
+
"#{Time.now.utc.strftime('%Y%m%d-%H%M%S')}-#{Process.pid}"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# ---- yaşam döngüsü -------------------------------------------------
|
|
78
|
+
|
|
79
|
+
def start!
|
|
80
|
+
subscribe_events
|
|
81
|
+
@worker = Thread.new { run_worker }
|
|
82
|
+
@worker.name = "rubik-vakanuvis" if @worker.respond_to?(:name=)
|
|
83
|
+
self
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def stopped? = @monitor.synchronize { @stopped }
|
|
87
|
+
|
|
88
|
+
# Dispose yolu: FIFO'daki her şey (STOP dahil öncesindeki olaylar)
|
|
89
|
+
# işlenir, sonra thread sonlanır. Kalan olay yarımda kalmaz; thread
|
|
90
|
+
# 2 saniyede durmazsa (yavaş bir flush/spawn içinde) kill gelir ve
|
|
91
|
+
# işlenemeyen olay sayısı dmesg'e düşer — sessiz kayıp yok.
|
|
92
|
+
def shutdown
|
|
93
|
+
@monitor.synchronize { @stopped = true }
|
|
94
|
+
@monitor.synchronize { @busy += 1 } # STOP işaretinin de işleniş sayılır
|
|
95
|
+
@queue.push(STOP)
|
|
96
|
+
@worker&.join(2)
|
|
97
|
+
if @worker&.alive?
|
|
98
|
+
Vakanuvis.log_via(@dmesg, :warn, "worker did not stop in 2s — killing with #{queue_size} event(s) unprocessed")
|
|
99
|
+
@worker.kill
|
|
100
|
+
end
|
|
101
|
+
@worker = nil
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def wait_idle(timeout: 2.0)
|
|
105
|
+
deadline = Time.now + timeout
|
|
106
|
+
sleep 0.01 while busy? && Time.now < deadline
|
|
107
|
+
!busy?
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Kuyrukta + uçuştaki olay sayısı (push'ta artar, işlenince/düşürülünce
|
|
111
|
+
# azalır) — pop ile işaretleme arasındaki yarış penceresini kapatır.
|
|
112
|
+
def busy? = @monitor.synchronize { @busy.positive? }
|
|
113
|
+
|
|
114
|
+
# ---- olay giriş kapıları (kworker thread'inde çağrılır) -----------
|
|
115
|
+
|
|
116
|
+
def enqueue_event(kind, payload)
|
|
117
|
+
return false if stopped?
|
|
118
|
+
|
|
119
|
+
unless payload.is_a?(Hash)
|
|
120
|
+
Vakanuvis.log_via(@dmesg, :warn, "discarded non-hash #{kind} event (#{payload.class})")
|
|
121
|
+
return false
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
drop_oldest! if @queue.size >= @max_queue
|
|
125
|
+
@monitor.synchronize { @busy += 1 }
|
|
126
|
+
@queue.push([kind, payload])
|
|
127
|
+
true
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def queue_size = @queue.size
|
|
131
|
+
|
|
132
|
+
# ---- talep düzlemi (maestro aracı buraya delege eder; K7) ---------
|
|
133
|
+
|
|
134
|
+
# Tam deterministik, LLM'siz: Store#search_ranked BM25-lite sırası +
|
|
135
|
+
# RECALL_TYPES kısıtı + ilk RECALL_LIMIT kayıt ⇒ aynı depo ⇒ aynı
|
|
136
|
+
# digest. type: dizisi (v2 §4-V8) transcript/note gibi arama dışı
|
|
137
|
+
# tipleri hiç parse etmez; sıralama politika kararına aittir —
|
|
138
|
+
# Store#search'in [type,row] sözleşmesi değişmez.
|
|
139
|
+
def recall(query, types: RECALL_TYPES)
|
|
140
|
+
q = query.to_s.strip
|
|
141
|
+
raise ArgumentError, "recall: 'query' is required" if q.empty?
|
|
142
|
+
|
|
143
|
+
allowed = Array(types).map(&:to_s)
|
|
144
|
+
allowed = RECALL_TYPES if allowed.empty?
|
|
145
|
+
hits = @store.search_ranked(query: q, type: allowed).first(RECALL_LIMIT)
|
|
146
|
+
unless hits.empty?
|
|
147
|
+
lines = hits.map { |r| recall_line(r) }
|
|
148
|
+
header = "Recalled #{hits.size} entit#{hits.size == 1 ? 'y' : 'ies'} for '#{AnnalWriter.one_line(q, 80)}':"
|
|
149
|
+
return "#{header}\n#{lines.join("\n")}\nRead a full record with read_file(path: '<uri above>')."
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
relaxed_digest(q, allowed)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# record tool thread'inden, sweep worker'dan koşar — get-then-put
|
|
156
|
+
# dansını tek kilide alınca çift satır açma yarışı kapanır (store
|
|
157
|
+
# monitoru asla geri çağırmadığı için kilit sırası tekyönlü).
|
|
158
|
+
def record(topic:, body:, links: {}, tags: [])
|
|
159
|
+
@monitor.synchronize { MemorySteward.record(@store, topic: topic, body: body, links: links, tags: tags) }
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def forget(ref:)
|
|
163
|
+
raise ArgumentError, "forget: 'ref' is required" if ref.to_s.strip.empty?
|
|
164
|
+
|
|
165
|
+
MemorySteward.forget(@store, ref: ref, retention: @retention)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Asenkron (K9): istek kuyruğa yazılır, maestro'ya deterministik
|
|
169
|
+
# makbuz döner — turn'ü asla LLM bekletilmez. Sınır nöbetçisi:
|
|
170
|
+
# boş sorgu burada patlar (tool katmanı yakalar), gerisi Gate'e
|
|
171
|
+
# delege (dedupe + doğum + makbuz orada).
|
|
172
|
+
def digest(query)
|
|
173
|
+
q = query.to_s.strip
|
|
174
|
+
raise ArgumentError, "digest: 'query' is required" if q.empty?
|
|
175
|
+
|
|
176
|
+
@gate.submit(q)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# İş kuyruğu görünürlüğü (issue #100 P2b): "yavaş kuyruk mu, düşen
|
|
180
|
+
# iş mi" ayırt edilemezdi — makbuzdan sonra recall, derleme inene
|
|
181
|
+
# dek hiçbir şey göstermiyordu. Deterministik dizgi.
|
|
182
|
+
def digest_status
|
|
183
|
+
targets = @gate.job_targets
|
|
184
|
+
if targets[:queued].empty? && targets[:in_flight].empty?
|
|
185
|
+
return "Digest queue: nothing queued, nothing in flight. " \
|
|
186
|
+
"recall shows compiled pages; a submitted digest appears here until it compiles."
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
"Digest queue: #{targets[:queued].size} queued (#{targets[:queued].join(', ')}), " \
|
|
190
|
+
"#{targets[:in_flight].size} in flight (#{targets[:in_flight].join(', ')})."
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Soğuk-başlangıç yönelimi (issue #99 P2): ilk sorgu sözcük dağı
|
|
194
|
+
# tahmin etmek zorunda kalmadan hangi konular var? Aktif ve mem.*
|
|
195
|
+
# bağlı kartlar [ref, row] sırasıyla — deterministik.
|
|
196
|
+
def topics
|
|
197
|
+
bound = @store.refs.select { |ref, _| Vocabulary.memory_ref?(ref) }
|
|
198
|
+
cards = @store.list(type: "memory").select do |s|
|
|
199
|
+
s.status == Vocabulary::STATUS_ACTIVE && bound.value?(s.id)
|
|
200
|
+
end
|
|
201
|
+
if cards.empty?
|
|
202
|
+
return "No memory topics recorded yet — teach the chronicler with " \
|
|
203
|
+
"vakanuvis(action: 'record', topic: ..., body: ...)."
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
lines = cards.sort_by { |s| [bound.key(s.id), s.row] }.filter_map do |s|
|
|
207
|
+
record = @store.get(type: "memory", id: s.id)
|
|
208
|
+
next unless record # list→get arası silme yarışı (review L2)
|
|
209
|
+
|
|
210
|
+
"- #{bound.key(s.id)} — #{s.title} — #{first_body_line(record)} — #{@store.uri_for('memory', s.id)}"
|
|
211
|
+
end
|
|
212
|
+
"Recorded topics (#{lines.size}):\n#{lines.join("\n")}"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
private
|
|
216
|
+
|
|
217
|
+
# v2 §4-V2 + issue #99 P2: AND boşluğu tek tip körlük değil — en
|
|
218
|
+
# yakın kısmi eşleşmeler dürüstçe "(fallback)" damgasıyla sunulur
|
|
219
|
+
# (gerçek eşleşme değildir), transcript izleri kapsam ipucu olarak
|
|
220
|
+
# sayılır. Ne fallback ne transcript izi varsa bugünkü dil/terim
|
|
221
|
+
# yönlendirmesi birebir döner (keyword-AND'da en sık neden TR
|
|
222
|
+
# sorgu ↔ EN gövde uyuşmazlığıdır; yanlış "hemen yeniden kaydet"
|
|
223
|
+
# yönlendirmesi burada kesilir).
|
|
224
|
+
def relaxed_digest(query, allowed)
|
|
225
|
+
q = query.to_s.strip
|
|
226
|
+
# Süpersede edilmiş sayılar "en yakın kısmi eşleşme" olarak
|
|
227
|
+
# sunulmaz (review M1) — emekli bir sayfayı okumaya davet etmek,
|
|
228
|
+
# issue #100'ün "iki kopya, biri bozuk" bulgusunun aynısıdır.
|
|
229
|
+
relaxed = @store.search_ranked(query: q, type: allowed, require_all: false)
|
|
230
|
+
.reject { |r| r["status"] == Vocabulary::STATUS_SUPERSEDED }
|
|
231
|
+
.first(RECALL_FALLBACK_LIMIT)
|
|
232
|
+
transcripts = @store.search(query: q, type: "transcript")
|
|
233
|
+
if relaxed.empty? && transcripts.empty?
|
|
234
|
+
return "No chronicled knowledge matches '#{AnnalWriter.one_line(q, 80)}'. Recall is keyword-AND: retry with " \
|
|
235
|
+
"fewer or other-language terms first (e.g. the TR/EN Keywords: lines). If the knowledge is genuinely " \
|
|
236
|
+
"missing, record it (vakanuvis action 'record') or queue a digest."
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
parts = []
|
|
240
|
+
# Başlık + read_file ayağı yalnız gerçek fallback izi varken
|
|
241
|
+
# basılır (review CR#1: "yukarıdaki uri" diyecek uri yokken
|
|
242
|
+
# olmayan bir talimat modele verilmesin).
|
|
243
|
+
unless relaxed.empty?
|
|
244
|
+
parts << "No exact keyword-AND match for '#{AnnalWriter.one_line(q, 80)}' — closest partial matches (fallback, not true matches):"
|
|
245
|
+
parts.concat(relaxed.map { |r| "- (fallback) #{recall_line(r)}" })
|
|
246
|
+
end
|
|
247
|
+
unless transcripts.empty?
|
|
248
|
+
parts << "#{transcripts.size} transcript entr#{transcripts.size == 1 ? 'y' : 'ies'} mention these terms — " \
|
|
249
|
+
"the raw session logs may hold it; queue a digest to compile them."
|
|
250
|
+
end
|
|
251
|
+
parts << "If none of these help, retry with fewer or other-language terms (the TR/EN Keywords: lines), " \
|
|
252
|
+
"record the knowledge, or queue a digest."
|
|
253
|
+
parts << "Read a full record with read_file(path: '<uri above>')." unless relaxed.empty?
|
|
254
|
+
parts.join("\n")
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def first_body_line(record)
|
|
258
|
+
slice = record["body"].to_s.lines.map(&:strip).reject(&:empty?).first.to_s.slice(0, RECALL_BODY_SLICE)
|
|
259
|
+
slice.empty? ? "(no body)" : slice
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def recall_line(record)
|
|
263
|
+
"- #{record['title']} [#{record['id']}] #{first_body_line(record)} — #{@store.uri_for(record['type'], record['id'])}"
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def subscribe_events
|
|
267
|
+
return unless @event_bus.respond_to?(:subscribe)
|
|
268
|
+
|
|
269
|
+
# Aboneliğin geri alımı ctx.on_dispose ile aynı nefeste (§1A):
|
|
270
|
+
# dispatcher subscribe'a verilen context'e unregister inverse'i
|
|
271
|
+
# kendisi bağlar.
|
|
272
|
+
@event_bus.subscribe(SUBAGENT_EVENT, context: @context) { |p| enqueue_event(:subagent, p) }
|
|
273
|
+
@event_bus.subscribe(GOAL_EVENT, context: @context) { |p| enqueue_event(:goal, p) }
|
|
274
|
+
return unless @root_turns
|
|
275
|
+
|
|
276
|
+
# Kök-turn kapısı knob arkasında (R7): kapalıyken abonelik hiç
|
|
277
|
+
# kurulmaz, :turn_finished olayı kronikçiye akmaz.
|
|
278
|
+
@event_bus.subscribe(TURN_EVENT, context: @context) { |p| enqueue_event(:turn, p) }
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Taşma düşürmesi asla STOP işaretini yutamaz (yutsa worker hiç
|
|
282
|
+
# sonlanamazdı): STOP geldiyse kuyruğun sonuna geri konur.
|
|
283
|
+
def drop_oldest!
|
|
284
|
+
item = @queue.pop(true)
|
|
285
|
+
if item == STOP
|
|
286
|
+
@queue.push(STOP)
|
|
287
|
+
else
|
|
288
|
+
@monitor.synchronize do
|
|
289
|
+
@dropped += 1
|
|
290
|
+
@busy -= 1
|
|
291
|
+
end
|
|
292
|
+
Vakanuvis.log_via(@dmesg, :warn, "event queue overflow — dropped oldest (total: #{@dropped})")
|
|
293
|
+
end
|
|
294
|
+
rescue ThreadError
|
|
295
|
+
nil
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def run_worker
|
|
299
|
+
loop do
|
|
300
|
+
kind, payload = @queue.pop
|
|
301
|
+
break if kind == STOP
|
|
302
|
+
|
|
303
|
+
handle_event(kind, payload)
|
|
304
|
+
ensure
|
|
305
|
+
@monitor.synchronize { @busy -= 1 }
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# TranscriptIndexer disiplini: kronikçinin kendi hatası akışı
|
|
310
|
+
# asla bozmaz — uyarı düşer, sonraki olay işlenir.
|
|
311
|
+
def handle_event(kind, payload)
|
|
312
|
+
case kind
|
|
313
|
+
when :subagent then handle_subagent(payload)
|
|
314
|
+
when :goal then handle_goal(payload)
|
|
315
|
+
when :turn then handle_turn(payload)
|
|
316
|
+
end
|
|
317
|
+
rescue StandardError => e
|
|
318
|
+
Vakanuvis.log_via(@dmesg, :warn, "event handling failed (#{kind}): #{e.class}: #{e.message}")
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def handle_subagent(payload)
|
|
322
|
+
# Önce kronik (Düzlem 1 garantisi), sonra hasat (Düzlem 2): hasat
|
|
323
|
+
# patlarsa bile olayın annal satırı yazılmış olur — deterministik
|
|
324
|
+
# katman LLM katmanının hatasından zarar görmez.
|
|
325
|
+
seq = next_seq
|
|
326
|
+
line = AnnalWriter.subagent_line(seq, payload)
|
|
327
|
+
append_session([[event_key(payload), line]])
|
|
328
|
+
@gate.completion_event(payload)
|
|
329
|
+
maintain if flush_due?
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# Kök-turn kroniği: ordinal session-slug süreç-başına mint
|
|
333
|
+
# edildiğinden harness-lokal sayaç yeterli (yeni süreç = yeni
|
|
334
|
+
# session annal; goal sırasındaki mağazadan-türetme burada
|
|
335
|
+
# gereksiz). Turn olayı flush sayacına sayılır ama koşulsuz
|
|
336
|
+
# maintain tetiklemez — turn sıklığı yüksek (R8).
|
|
337
|
+
def handle_turn(payload)
|
|
338
|
+
ordinal = @monitor.synchronize { @turn_ordinal += 1 }
|
|
339
|
+
seq = next_seq
|
|
340
|
+
append_session([AnnalWriter.turn_line(seq, ordinal, payload)])
|
|
341
|
+
maintain if flush_due?
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def handle_goal(payload)
|
|
345
|
+
ordinal = next_goal_ordinal
|
|
346
|
+
enriched = payload.merge(goal_ordinal: ordinal)
|
|
347
|
+
seq = next_seq
|
|
348
|
+
goal_body = [AnnalWriter.goal_header_line(enriched[:goal])] +
|
|
349
|
+
AnnalWriter.goal_lines(seq, enriched).map { |_k, l| l }
|
|
350
|
+
Vakanuvis.upsert(@store, type: "note", ref: Vocabulary.goal_ref(ordinal),
|
|
351
|
+
title: AnnalWriter.goal_title(ordinal, enriched[:goal]),
|
|
352
|
+
body: goal_body.join("\n"), tags: Vocabulary::ANNAL_TAGS + [Vocabulary::GOAL_TAG],
|
|
353
|
+
links: { "session" => Vocabulary.session_ref(@session_slug) })
|
|
354
|
+
append_session(AnnalWriter.goal_lines(seq, enriched))
|
|
355
|
+
maintain
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# Goal sırası mağazadan türetilir (refs'teki en büyük annal.goal.<n>
|
|
359
|
+
# + 1): süreç yeniden başlasa bile aynı depoda numara çakışmaz —
|
|
360
|
+
# determinizm depo durumundan, saatlerden değil.
|
|
361
|
+
def next_goal_ordinal
|
|
362
|
+
@monitor.synchronize do
|
|
363
|
+
max_ref = @store.refs.keys.filter_map { |k| Vocabulary.goal_ordinal(k) }.max.to_i
|
|
364
|
+
@goal_ordinal = [@goal_ordinal, max_ref].max + 1
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# Flush sayacı (R8): subagent ve turn olayları aynı ölçüde sayılır;
|
|
369
|
+
# flush_events eşiği aşılınca true döner ve sayaç sıfırlanır.
|
|
370
|
+
def flush_due?
|
|
371
|
+
@monitor.synchronize do
|
|
372
|
+
@events_since_distill += 1
|
|
373
|
+
crossed = @events_since_distill >= @flush_events
|
|
374
|
+
@events_since_distill = 0 if crossed
|
|
375
|
+
crossed
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# Flush bakımı (yalnızca flush noktalarında — flush_events eşiği
|
|
380
|
+
# aşımı veya goal bitişi): kaçak memory süpürme (K9) + wiki home
|
|
381
|
+
# yeniden hesap + damıtıcı doğum kararı. v2 §4-V5: toplu doğum
|
|
382
|
+
# yalnızca kirli işaretle — kalıcı içerik değişmediyse (yalnızca
|
|
383
|
+
# turn satırları biriktiyse) damıtıcı yakılmaz.
|
|
384
|
+
def maintain
|
|
385
|
+
# Sweep record() ile aynı kilide koşar (tek-yazar disiplini):
|
|
386
|
+
# get-then-put dansı çift satır açamaz.
|
|
387
|
+
swept = @monitor.synchronize { MemorySteward.sweep_rogue_memory(@store) }
|
|
388
|
+
Vakanuvis.log_via(@dmesg, :info, "steward swept #{swept} rogue memory row(s)") if swept.positive?
|
|
389
|
+
WikiSkeleton.rebuild_home(@store)
|
|
390
|
+
@gate.flush_tick
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def append_session(lines)
|
|
394
|
+
overflow = nil
|
|
395
|
+
@monitor.synchronize do
|
|
396
|
+
if @dropped.positive?
|
|
397
|
+
seq = @seq += 1
|
|
398
|
+
overflow = [["dropped.#{seq}", "- [#{seq}] #{@dropped} event(s) dropped — queue overflow (event=dropped.#{seq})"]]
|
|
399
|
+
@dropped = 0
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
AnnalWriter.append(@store, ref: Vocabulary.session_ref(@session_slug),
|
|
403
|
+
title: AnnalWriter.session_title(@session_slug),
|
|
404
|
+
lines: lines + overflow.to_a)
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def next_seq = @monitor.synchronize { @seq += 1 }
|
|
408
|
+
|
|
409
|
+
def event_key(payload)
|
|
410
|
+
sid = payload[:storage_id].to_s
|
|
411
|
+
sid = payload[:id].to_s if sid.empty?
|
|
412
|
+
"subagent.#{sid}.#{payload[:state]}"
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def fetch_cfg(key, default)
|
|
416
|
+
@config.respond_to?(:fetch) ? @config.fetch(key, default) : default
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
end
|