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,399 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "time"
|
|
4
|
+
require "json"
|
|
5
|
+
require_relative "../../prompt/context_compactor"
|
|
6
|
+
require_relative "../../prompt/conversation_rows"
|
|
7
|
+
require_relative "../../uris"
|
|
8
|
+
|
|
9
|
+
module Rubik
|
|
10
|
+
module Orchestrator
|
|
11
|
+
module GoalLedger
|
|
12
|
+
# !goal döngüsünün yapılandırılmış ΔΣ defteri (Σ) — SKILL.state
|
|
13
|
+
# uyarlamasının durumlu yarısı. C kütüphanesi (Patch/Schema/Validator)
|
|
14
|
+
# SAFTI; burada yaşam döngüsü durumu yaşar: Monitor korumalı doküman,
|
|
15
|
+
# ardışık-ret bütçesi, storage revizyon zinciri (her kabul YENİ satır;
|
|
16
|
+
# `goal-ledger` ref'i daima son revizyonu gösterir) ve dmesg telemetrisi.
|
|
17
|
+
#
|
|
18
|
+
# Thread modeli (plan A kuralı): yazmalar YALNIZ turn thread'indedir
|
|
19
|
+
# (araç execute, step-top refresh, finalize); tüm okumalar Monitor
|
|
20
|
+
# altındadır — rubik://goal/ledger read_file her an güvenlidir
|
|
21
|
+
# (GoalState mold'u). Arkaplan thread'i yoktur.
|
|
22
|
+
#
|
|
23
|
+
# Degradasyon sözleşmesi: Store yoksa/başarısızsa satır YAZILMAZ, tek
|
|
24
|
+
# dmesg warn düşer ve Σ bellekte yaşamaya devam eder — özellik asla
|
|
25
|
+
# storage hatasıyla çökmez ("prompt'tan atmak ≠ kayıttan atmak":
|
|
26
|
+
# transcripts/ToolHistory bu sınıfa hiç girmez).
|
|
27
|
+
class Ledger
|
|
28
|
+
Result = Struct.new(:status, :reasons, :rev, :uri, keyword_init: true)
|
|
29
|
+
|
|
30
|
+
# A planı üretim şemasını SAHİPLENİR: goal skeleton örnek örneği
|
|
31
|
+
# üretim yüzeyi olur (frozen; verify alanları optional+allow_null —
|
|
32
|
+
# re-verification planı doldurur).
|
|
33
|
+
PRODUCTION_SCHEMA = Schema.goal_skeleton
|
|
34
|
+
|
|
35
|
+
# rev = Σ sürüm SSOT'u (doc["rev"]) — ObservationProjection ve
|
|
36
|
+
# kullanıcılar Monitor altında okur (@rev ivarı YOKTUR; atanmaz).
|
|
37
|
+
def rev
|
|
38
|
+
@monitor.synchronize { @doc["rev"] }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
attr_reader :event_bus
|
|
42
|
+
|
|
43
|
+
def initialize(goal_text:, max_retries: DEFAULT_MAX_RETRIES, store: nil, dmesg: nil, event_bus: nil)
|
|
44
|
+
@monitor = Monitor.new
|
|
45
|
+
@max_retries = max_retries.to_i
|
|
46
|
+
@store = store
|
|
47
|
+
@dmesg = dmesg
|
|
48
|
+
@event_bus = event_bus
|
|
49
|
+
@rejections = 0
|
|
50
|
+
@storage_id = nil
|
|
51
|
+
@last_links = {}
|
|
52
|
+
@finalized = false
|
|
53
|
+
@updated_at = nil
|
|
54
|
+
@last_drift_note = nil
|
|
55
|
+
@doc = seed_doc(goal_text)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# ------------------------------------------------------------------
|
|
59
|
+
# Sınıf yüzü: çift-diyalekt knob okuması (pruner Service mold'u —
|
|
60
|
+
# Rubik::Config dotted-path böler, düz Hash tek anahtar sayar) ve
|
|
61
|
+
# oluşturma fabrikası. Konum: Ledger üzerinde, çünkü GoalLedger
|
|
62
|
+
# modülü C sabitlerinin evi olmayı sürdürür (shim < 90 hygiene pin).
|
|
63
|
+
class << self
|
|
64
|
+
def enabled?(config = nil)
|
|
65
|
+
config_get(config, "goal.ledger.enabled", true)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# B'nin knob'u (re-verification): enabled kapalıyken İNERTTİR
|
|
69
|
+
# (coeffect hiç provide edilmez → verifier girişi no-op).
|
|
70
|
+
def verify_enabled?(config = nil)
|
|
71
|
+
config_get(config, "goal.ledger.verify", true)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def max_patch_retries(config = nil)
|
|
75
|
+
config_get(config, "goal.ledger.max_patch_retries", DEFAULT_MAX_RETRIES).to_i
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def create!(goal_text:, config: nil, store: nil, dmesg: nil, event_bus: nil)
|
|
79
|
+
ledger = new(goal_text: goal_text.to_s, max_retries: max_patch_retries(config),
|
|
80
|
+
store: store, dmesg: dmesg, event_bus: event_bus)
|
|
81
|
+
ledger.persist_seed!
|
|
82
|
+
ledger
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def config_get(config, key, default)
|
|
86
|
+
return default if config.nil?
|
|
87
|
+
|
|
88
|
+
val = if config.is_a?(Hash)
|
|
89
|
+
config.dig(*key.to_s.split("."))
|
|
90
|
+
elsif config.respond_to?(:fetch)
|
|
91
|
+
config.fetch(key, nil)
|
|
92
|
+
end
|
|
93
|
+
val.nil? ? default : val
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def persist_seed!
|
|
98
|
+
row = put_row(title: row_title, body: canonical_json, tags: ["goal-ledger"], links: {}, ref: "goal-ledger")
|
|
99
|
+
@storage_id = row["id"] if row
|
|
100
|
+
degrade_storage_once unless row
|
|
101
|
+
rescue StandardError => e
|
|
102
|
+
degrade("seed revision row", e)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Tek giriş: patch metni (ya da Patch VO) → sınıflandırılmış sonuç.
|
|
106
|
+
# Ret sayacı ardışıktır; başarı sıfırlar; bütçe aşımında :exhausted
|
|
107
|
+
# döner ve ESKİ Σ korunur (goal loop asla brick olmaz).
|
|
108
|
+
def update(patch_or_text)
|
|
109
|
+
verdict = Validator.validate(patch_or_text, schema: PRODUCTION_SCHEMA, current_doc: @doc)
|
|
110
|
+
@monitor.synchronize do
|
|
111
|
+
if verdict[0] == :ok
|
|
112
|
+
apply!(verdict[1])
|
|
113
|
+
elsif @rejections >= @max_retries
|
|
114
|
+
# C sözleşmesi: ardışık ret bütçesi dolu — ESKİ Σ korunur ve
|
|
115
|
+
# dürüst tükenme döner; geçerli bir patch geldiğinde kabul
|
|
116
|
+
# yolu yeniden açılır (goal loop asla brick olmaz).
|
|
117
|
+
exhausted_result
|
|
118
|
+
else
|
|
119
|
+
@rejections += 1
|
|
120
|
+
log(:patch_rejected, "patch rejected (#{@rejections}/#{@max_retries} budget): " \
|
|
121
|
+
"#{verdict[1].first[:class]} — #{verdict[1].first[:message]}")
|
|
122
|
+
Result.new(status: :rejected, reasons: verdict[1], rev: @doc["rev"], uri: ledger_uri)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Adım başı yenileme: mevcut Σ satırlarını düşürüp TEK güncel satırı
|
|
128
|
+
# <user_task> hedef satırının peşine yerleştirir (tek-Σ-satırı
|
|
129
|
+
# invaryantı — pinli satırlar compaction'ı baypas ederek şişemez).
|
|
130
|
+
# Ledger satırı role "user" zorunludur: EmergencyCompactor hard
|
|
131
|
+
# tier'ı user satırlarına dokunmaz (emergency_compactor.rb:59-77).
|
|
132
|
+
def refresh_in_history!(history)
|
|
133
|
+
return if history.nil? || !history.respond_to?(:reject!)
|
|
134
|
+
|
|
135
|
+
@monitor.synchronize do
|
|
136
|
+
history.reject! { |msg| ledger_row?(msg) || drift_row?(msg) }
|
|
137
|
+
anchor = history.rindex { |msg| Prompt::ContextCompactor.goal_message?(msg) }
|
|
138
|
+
if anchor
|
|
139
|
+
history.insert(anchor + 1, { role: "user", content: render_xml })
|
|
140
|
+
history.insert(anchor + 2, { role: "user", content: @last_drift_note }) if @last_drift_note
|
|
141
|
+
else
|
|
142
|
+
history << { role: "user", content: render_xml }
|
|
143
|
+
history << { role: "user", content: @last_drift_note } if @last_drift_note
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Terminal damga (complete!/fail!/stop! sonrası): son revizyon
|
|
149
|
+
# satırına status yazılır (id korunur — version++), idempotenttir.
|
|
150
|
+
def finalize!(status)
|
|
151
|
+
@monitor.synchronize do
|
|
152
|
+
return final_result if @finalized
|
|
153
|
+
|
|
154
|
+
@finalized = true
|
|
155
|
+
if @store && @storage_id
|
|
156
|
+
links = @last_links.merge("finish_status" => status.to_s)
|
|
157
|
+
row = @store.put(type: "goal", id: @storage_id, title: row_title, body: canonical_json,
|
|
158
|
+
tags: ["goal-ledger", "final"], links: links, status: status.to_s)
|
|
159
|
+
@storage_id = row["id"]
|
|
160
|
+
else
|
|
161
|
+
degrade_storage_once
|
|
162
|
+
end
|
|
163
|
+
log(:finalized, "goal ledger finalized at rev #{@doc['rev']} (status: #{status})")
|
|
164
|
+
final_result
|
|
165
|
+
end
|
|
166
|
+
rescue StandardError => e
|
|
167
|
+
degrade("final status stamp", e)
|
|
168
|
+
@monitor.synchronize { final_result }
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# B (re-verification): iç mutasyon girişi — drift flip'leri MODEL
|
|
172
|
+
# ΔΣ validasyonunu ATLAR (bu bir model patch'i değildir) ama rev
|
|
173
|
+
# BUMP EDİLİR: :conflict, drift sinyalinin ta kendisidir (model
|
|
174
|
+
# bayat rev ile patch gönderir → conflict → yeniden okur → stale
|
|
175
|
+
# bayraklarını GÖRÜR — paper'ın zero-recovery mekanizması). Tek
|
|
176
|
+
# yazar disiplini: persistence A'nın kendi put yolundan geçer.
|
|
177
|
+
def apply_internal_drift!(flips)
|
|
178
|
+
@monitor.synchronize do
|
|
179
|
+
flips.each do |flip|
|
|
180
|
+
item = @doc[flip[:collection]]&.[](flip[:index])
|
|
181
|
+
next unless item.is_a?(Hash)
|
|
182
|
+
|
|
183
|
+
item["status"] = "stale" if flip[:collection] == "tasks"
|
|
184
|
+
verify_h = item["verify"].is_a?(Hash) ? item["verify"].dup : {}
|
|
185
|
+
verify_h["last_check"] = { "state" => "failed", "from" => flip[:from].to_s,
|
|
186
|
+
"reason" => flip[:reason].to_s, "at" => flip[:at].to_s }
|
|
187
|
+
item["verify"] = verify_h
|
|
188
|
+
end
|
|
189
|
+
return nil if flips.empty?
|
|
190
|
+
|
|
191
|
+
@doc["rev"] += 1
|
|
192
|
+
@updated_at = Time.now.utc
|
|
193
|
+
if @store && @storage_id
|
|
194
|
+
@store.put(type: "goal", id: @storage_id, title: row_title, body: canonical_json,
|
|
195
|
+
tags: ["goal-ledger"], links: @last_links, status: "active")
|
|
196
|
+
end
|
|
197
|
+
@last_drift_note = DriftNote.render(snap: deep_dup(@doc))
|
|
198
|
+
log(:ledger_drift, "internal drift applied: #{flips.size} flip(s), rev #{@doc['rev']}")
|
|
199
|
+
{ rev: @doc["rev"], flipped: flips.size }
|
|
200
|
+
end
|
|
201
|
+
rescue StandardError => e
|
|
202
|
+
degrade("drift persistence", e)
|
|
203
|
+
nil
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Verifier girişi: Monitor'lu FROZEN kopya — FS IO kilit dışı
|
|
207
|
+
# değerlendirilir (verifier.rb disiplini).
|
|
208
|
+
def snapshot_doc
|
|
209
|
+
@monitor.synchronize { deep_freeze(deep_dup(@doc)) }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# Test/seam gözlemcisi: güncel drift notu (yoksa nil).
|
|
213
|
+
def render_drift_note
|
|
214
|
+
@monitor.synchronize { DriftNote.render(snap: deep_dup(@doc)) }
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def to_xml
|
|
218
|
+
@monitor.synchronize { render_xml }
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# §4.1 render: GoalState#to_xml mold'u — tüm metin xml_escape'ten
|
|
222
|
+
# geçer; boş koleksiyonların etiketi HİÇ basılmaz ("yokluk, boşluk
|
|
223
|
+
# değil" kuralı); optional attribute'lar (storage_id, verify) yalnız
|
|
224
|
+
# set iken basılır.
|
|
225
|
+
def render_xml
|
|
226
|
+
esc = Rubik::Platform.method(:xml_escape)
|
|
227
|
+
attrs = %(rev="#{@doc['rev']}")
|
|
228
|
+
attrs += %( storage_id="#{esc.call(@storage_id)}") if @storage_id
|
|
229
|
+
attrs += %( updated_at="#{@updated_at.utc.iso8601}") if @updated_at
|
|
230
|
+
body = []
|
|
231
|
+
body << %( <objective>#{esc.call(@doc['objective'].to_s)}</objective>)
|
|
232
|
+
tasks = @doc["tasks"].map do |task|
|
|
233
|
+
text = task["title"].to_s
|
|
234
|
+
text += " — notes: #{task['notes']}" if task["notes"]
|
|
235
|
+
attrs_t = %(id="#{esc.call(task['id'].to_s)}" status="#{esc.call(task['status'].to_s)}")
|
|
236
|
+
attrs_t += %( verify="#{esc.call(task['verify'].to_s)}") if task["verify"]
|
|
237
|
+
task["title"] || task["notes"] ? %( <task #{attrs_t}>#{esc.call(text)}</task>) : %( <task #{attrs_t} />)
|
|
238
|
+
end
|
|
239
|
+
body << "<tasks>\n#{tasks.join("\n")}\n </tasks>" unless tasks.empty?
|
|
240
|
+
findings = @doc["findings"].map do |f|
|
|
241
|
+
%( <finding id="#{esc.call(f['id'].to_s)}" source_step="#{f['source_step']}">#{esc.call(f['text'].to_s)}</finding>)
|
|
242
|
+
end
|
|
243
|
+
body << "<findings>\n#{findings.join("\n")}\n </findings>" unless findings.empty?
|
|
244
|
+
decisions = @doc["decisions"].map do |dec|
|
|
245
|
+
attrs_d = %(id="#{esc.call(dec['id'].to_s)}")
|
|
246
|
+
attrs_d += %( rationale="#{esc.call(dec['rationale'].to_s)}") if dec["rationale"]
|
|
247
|
+
%( <decision #{attrs_d}>#{esc.call(dec['text'].to_s)}</decision>)
|
|
248
|
+
end
|
|
249
|
+
body << "<decisions>\n#{decisions.join("\n")}\n </decisions>" unless decisions.empty?
|
|
250
|
+
questions = @doc["open_questions"].map { |q| %( <question>#{esc.call(q.to_s)}</question>) }
|
|
251
|
+
body << "<open_questions>\n#{questions.join("\n")}\n </open_questions>" unless questions.empty?
|
|
252
|
+
notes = @doc["environment_notes"].map do |note|
|
|
253
|
+
attrs_n = %(id="#{esc.call(note['id'].to_s)}")
|
|
254
|
+
attrs_n += %( verify="#{esc.call(note['verify'].to_s)}") if note["verify"]
|
|
255
|
+
%( <note #{attrs_n}>#{esc.call(note['text'].to_s)}</note>)
|
|
256
|
+
end
|
|
257
|
+
body << "<environment_notes>\n#{notes.join("\n")}\n </environment_notes>" unless notes.empty?
|
|
258
|
+
<<~XML.strip
|
|
259
|
+
<goal_ledger #{attrs}>
|
|
260
|
+
#{body.join("\n")}
|
|
261
|
+
</goal_ledger>
|
|
262
|
+
XML
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def snapshot
|
|
266
|
+
@monitor.synchronize do
|
|
267
|
+
{ rev: @doc["rev"], objective: @doc["objective"], storage_id: @storage_id,
|
|
268
|
+
updated_at: @updated_at, finalized: @finalized, tasks: @doc["tasks"].size,
|
|
269
|
+
findings: @doc["findings"].size, decisions: @doc["decisions"].size,
|
|
270
|
+
open_questions: @doc["open_questions"].size, environment_notes: @doc["environment_notes"].size }
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
private
|
|
275
|
+
|
|
276
|
+
def seed_doc(goal_text)
|
|
277
|
+
{ "rev" => 0, "objective" => Rubik::Platform.safe_utf8(goal_text.to_s.slice(0, 400)),
|
|
278
|
+
"tasks" => [], "findings" => [], "decisions" => [],
|
|
279
|
+
"open_questions" => [], "environment_notes" => [] }
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def apply!(new_doc)
|
|
283
|
+
# Validator deep-frozen çıkarır (VO sözleşmesi); durum sahibi
|
|
284
|
+
# ledger'ın ÇALIŞMA kopyası mutable kalmalıdır (apply_internal_drift!
|
|
285
|
+
# ve render_xml iç mutasyonu).
|
|
286
|
+
@doc = deep_dup(new_doc)
|
|
287
|
+
@rejections = 0
|
|
288
|
+
@updated_at = Time.now.utc
|
|
289
|
+
@last_drift_note = nil # model yeniden planladı — drift notu tek seferliktir
|
|
290
|
+
links = @storage_id ? { "ledger_prev" => @storage_id } : {}
|
|
291
|
+
row = put_row(title: row_title, body: canonical_json, tags: ["goal-ledger"], links: links, ref: "goal-ledger")
|
|
292
|
+
if row
|
|
293
|
+
@storage_id = row["id"]
|
|
294
|
+
@last_links = links
|
|
295
|
+
end
|
|
296
|
+
log(:patch_accepted, "goal ledger rev #{@doc['rev']} applied (#{row ? "stored #{@storage_id}" : 'memory-only'})")
|
|
297
|
+
Result.new(status: :applied, rev: @doc["rev"], uri: ledger_uri)
|
|
298
|
+
rescue StandardError => e
|
|
299
|
+
# Storage yazım başarısızlığı araç yolunu BOGMAMALI: Σ zaten
|
|
300
|
+
# uygulandı (atomik validate-then-apply), yalnız kalıcılık düştü.
|
|
301
|
+
degrade("revision row", e)
|
|
302
|
+
Result.new(status: :applied, rev: @doc["rev"], uri: ledger_uri)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def exhausted_result
|
|
306
|
+
log(:patch_exhausted, "patch budget exhausted after #{@rejections} consecutive rejections; " \
|
|
307
|
+
"ledger unchanged at rev #{@doc['rev']}")
|
|
308
|
+
Result.new(status: :exhausted, rev: @doc["rev"], uri: ledger_uri)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def final_result
|
|
312
|
+
{ id: @storage_id, rev: @doc["rev"], uri: ledger_uri }
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def put_row(title:, body:, tags:, links:, ref: nil)
|
|
316
|
+
return nil unless @store
|
|
317
|
+
|
|
318
|
+
@store.put(type: "goal", title: title, body: body, tags: tags, links: links, ref: ref)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def canonical_json
|
|
322
|
+
Rubik::Platform.safe_json_generate(@doc)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def row_title
|
|
326
|
+
"ledger r#{@doc['rev']}: #{Rubik::Platform.safe_utf8(@doc['objective'].to_s.slice(0, 60))}"
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def ledger_uri
|
|
330
|
+
@storage_id ? Rubik::Uris.storage_entity(project: @store.project, type: "goal", id: @storage_id) : nil
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def ledger_row?(msg)
|
|
334
|
+
return false unless Prompt::ConversationRows.row?(msg)
|
|
335
|
+
|
|
336
|
+
content = Prompt::ConversationRows.content_of(msg).to_s
|
|
337
|
+
Prompt::ConversationRows.role_of(msg) == "user" && content.start_with?("<goal_ledger ") && content.include?("</goal_ledger>")
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def drift_row?(msg)
|
|
341
|
+
return false unless Prompt::ConversationRows.row?(msg)
|
|
342
|
+
|
|
343
|
+
content = Prompt::ConversationRows.content_of(msg).to_s
|
|
344
|
+
Prompt::ConversationRows.role_of(msg) == "user" && content.start_with?("<ledger_drift ") && content.include?("</ledger_drift>")
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def deep_dup(value)
|
|
348
|
+
case value
|
|
349
|
+
when Hash then value.to_a.to_h { |key, val| [key.dup, deep_dup(val)] }
|
|
350
|
+
when Array then value.map { |val| deep_dup(val) }
|
|
351
|
+
else value
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def deep_freeze(value)
|
|
356
|
+
case value
|
|
357
|
+
when Hash then value.each do |key, val|
|
|
358
|
+
deep_freeze(key)
|
|
359
|
+
deep_freeze(val)
|
|
360
|
+
end.freeze
|
|
361
|
+
when Array then value.each { |val| deep_freeze(val) }.freeze
|
|
362
|
+
when String then value.dup.freeze
|
|
363
|
+
else value
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def log(event, message)
|
|
368
|
+
unless @dmesg.respond_to?(:log)
|
|
369
|
+
# Dürüst degradasyon (review 2026-10-10): dmesg yokken telemetri
|
|
370
|
+
# sessizliğe gömülmesin — bir kez Log'a düş, sonra sessiz.
|
|
371
|
+
unless @dmesg_degraded
|
|
372
|
+
@dmesg_degraded = true
|
|
373
|
+
Rubik::Log.warn(:goal_ledger, "dmesg unavailable — goal ledger telemetry degraded to Log only")
|
|
374
|
+
end
|
|
375
|
+
return
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
@dmesg.log(:goal_ledger, :info, "[#{event}] #{message}")
|
|
379
|
+
rescue StandardError
|
|
380
|
+
Rubik::Log.warn(:goal_ledger, "[#{event}] #{message}")
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def degrade(what, error)
|
|
384
|
+
Rubik::Log.warn(:goal_ledger, "goal ledger #{what} unavailable (#{error.class}: #{error.message}); " \
|
|
385
|
+
"Σ continues in memory")
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# Depo yokluğu bilinçli degradasyondur ama SESSIZ olamaz (sözleşme:
|
|
389
|
+
# tek dürüst warn) — memoize'lı tek atış.
|
|
390
|
+
def degrade_storage_once
|
|
391
|
+
return if @storage_degraded
|
|
392
|
+
|
|
393
|
+
@storage_degraded = true
|
|
394
|
+
Rubik::Log.warn(:goal_ledger, "storage unavailable — goal ledger revisions stay memory-only (no audit rows)")
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
end
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../prompt/conversation_rows"
|
|
4
|
+
|
|
5
|
+
module Rubik
|
|
6
|
+
module Orchestrator
|
|
7
|
+
module GoalLedger
|
|
8
|
+
# Gözlem projeksiyonu (goal-ledger planı D): goal-loop observation'larının
|
|
9
|
+
# Σ üzerinden erken yapısal projeksiyonu — compaction'ın TIER 0'ı.
|
|
10
|
+
#
|
|
11
|
+
# Sözleşmeler: (1) yalnız goal-mode user kanalı (directive_for önkoşul
|
|
12
|
+
# matrisi: goal_mode? → knob'lar → coeffect → rev ≥ 1; herhangi biri
|
|
13
|
+
# yoksa nil → compaction byte-özdeş); (2) satır DÜŞÜRME YOK — yalnız
|
|
14
|
+
# content tombstone'u (tool_call_id pairing'i korunur, orphan-id 400
|
|
15
|
+
# riski yapısal olarak imkânsız); (3) kayıt katmanı dokunulmaz —
|
|
16
|
+
# tombstone yalnız compact_history'nin dup kopyasına yazılır
|
|
17
|
+
# ("prompt'tan atmak ≠ kayıttan atmak"); (4) worker thread yok —
|
|
18
|
+
# directive çözümü + uygulama yalnız turn thread'indedir.
|
|
19
|
+
module ObservationProjection
|
|
20
|
+
MODULE_NAME = "update_goal_ledger" # ledger tool adının SSOT'u (Declaration name ile aynı değer)
|
|
21
|
+
SKILL_TOOL_NAME = "skill" # progressive disclosure payload muafiyeti
|
|
22
|
+
|
|
23
|
+
module_function
|
|
24
|
+
|
|
25
|
+
# Önkoşul matrisi -> Directive | nil. Misconfiguration dürüstlüğü:
|
|
26
|
+
# observe_projection ON ama enabled OFF iken tek dmesg warn (çağrı
|
|
27
|
+
# yeri compaction'tır — doğal oran-limitli).
|
|
28
|
+
def directive_for(deps:)
|
|
29
|
+
config = deps.config
|
|
30
|
+
# goal_mode? yüklü olmayan test double'ları (ShellDeps method_missing
|
|
31
|
+
# raise eder) yapısal olarak dışla — non-goal muamelesi görsünler.
|
|
32
|
+
return nil unless deps.respond_to?(:goal_mode?) && deps.goal_mode?
|
|
33
|
+
|
|
34
|
+
enabled = Ledger.enabled?(config)
|
|
35
|
+
unless enabled
|
|
36
|
+
if config_get(config, "goal.ledger.observe_projection", true) && deps.respond_to?(:runtime_manager)
|
|
37
|
+
dmesg = Rubik::Coeffects.get(deps.runtime_manager&.context, Rubik::Coeffects::DMESG)
|
|
38
|
+
if dmesg.respond_to?(:log)
|
|
39
|
+
dmesg.log(:orchestrator, :warn,
|
|
40
|
+
"goal.ledger.observe_projection is ON but goal.ledger.enabled is OFF — observation projection inert")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
return nil
|
|
44
|
+
end
|
|
45
|
+
return nil unless config_get(config, "goal.ledger.observe_projection", true)
|
|
46
|
+
|
|
47
|
+
ledger = Rubik::Coeffects.get(deps.runtime_manager&.context, Rubik::Coeffects::GOAL_LEDGER)
|
|
48
|
+
return nil unless ledger.respond_to?(:rev)
|
|
49
|
+
return nil unless ledger.rev.is_a?(Integer) && ledger.rev >= 1
|
|
50
|
+
|
|
51
|
+
window = config_get(config, "goal.ledger.observe_window", 5).to_i
|
|
52
|
+
Directive.new(ledger: ledger, window: [window, 1].max, deps: deps)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def config_get(config, key, default)
|
|
56
|
+
return default if config.nil?
|
|
57
|
+
|
|
58
|
+
val = if config.is_a?(Hash)
|
|
59
|
+
config.dig(*key.to_s.split("."))
|
|
60
|
+
elsif config.respond_to?(:fetch)
|
|
61
|
+
config.fetch(key, nil)
|
|
62
|
+
end
|
|
63
|
+
val.nil? ? default : val
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Kural: anchor = en yeni KABUL edilmiş update_goal_ledger tool satırını
|
|
68
|
+
# içeren atomic unit (model o çağrıyı yaparken önceki tüm observation'ları
|
|
69
|
+
# görmüştü). Anchor'dan önceki birimler = banked; son K assistant-taşıyan
|
|
70
|
+
# birim + anchor + sonrası daima raw. Banked bölgedeki tool satırlarının
|
|
71
|
+
# content'i tombstone ile değişir; diğer anahtarlar bit-bit korunur.
|
|
72
|
+
class ObservationProjection::Directive
|
|
73
|
+
def initialize(ledger:, window:, deps:)
|
|
74
|
+
@ledger = ledger
|
|
75
|
+
@window = window
|
|
76
|
+
@deps = deps
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
attr_reader :rev
|
|
80
|
+
|
|
81
|
+
# -> {count:, rev:, reclaimed_tokens:} (rows KOPYASI üzerinde çalışır)
|
|
82
|
+
def apply!(rows)
|
|
83
|
+
before = Prompt::TokenEstimator.estimate_history(rows)
|
|
84
|
+
units = Prompt::ContextCompactor.group_atomic_units(rows)
|
|
85
|
+
anchor_idx = anchor_unit_index(units)
|
|
86
|
+
count = 0
|
|
87
|
+
rev_now = @ledger.rev
|
|
88
|
+
units.each_with_index do |unit, idx|
|
|
89
|
+
next unless banked?(idx, anchor_idx, units)
|
|
90
|
+
next if Prompt::ContextCompactor.unit_pins_goal?(unit) # <user_task> taşıyan birim asla emilmez
|
|
91
|
+
|
|
92
|
+
unit.each do |row|
|
|
93
|
+
count += 1 if tombstone!(row, rev_now)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
return { count: 0, rev: rev_now, reclaimed_tokens: 0 } if count.zero?
|
|
97
|
+
|
|
98
|
+
reclaimed = before - Prompt::TokenEstimator.estimate_history(rows)
|
|
99
|
+
observe(count, rev_now, reclaimed)
|
|
100
|
+
{ count: count, rev: rev_now, reclaimed_tokens: reclaimed }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
# En yeni accepted anchor biriminin index'i; hiç yoksa nil → no-op
|
|
106
|
+
# (anchor'sız banked bölge tanımsızdır — dürüst iç çekim yapmayız).
|
|
107
|
+
def anchor_unit_index(units)
|
|
108
|
+
units.rindex { |unit| unit.any? { |row| anchor_row?(row) } }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def anchor_row?(row)
|
|
112
|
+
return false unless Prompt::ConversationRows.row?(row)
|
|
113
|
+
|
|
114
|
+
Prompt::ConversationRows.role_of(row) == "tool" &&
|
|
115
|
+
tool_name_of(row) == ObservationProjection::MODULE_NAME &&
|
|
116
|
+
row[:status].to_s != "error" && row["status"].to_s != "error"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Recency: son K assistant-taşıyan birimin İLKİnden itibaren her şey
|
|
120
|
+
# (ve anchor'u ve sonrası) raw kalır; banked = idx < anchor VE
|
|
121
|
+
# idx < recency_start.
|
|
122
|
+
def banked?(idx, anchor_idx, units)
|
|
123
|
+
return false if anchor_idx.nil? || idx >= anchor_idx
|
|
124
|
+
|
|
125
|
+
assistant_units = units.each_index.select { |i| units[i].any? { |r| assistant_row?(r) } }
|
|
126
|
+
recency_start = assistant_units[[assistant_units.size - @window, 0].max] || units.size
|
|
127
|
+
idx < recency_start
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def assistant_row?(row)
|
|
131
|
+
Prompt::ConversationRows.row?(row) && Prompt::ConversationRows.role_of(row) == "assistant"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Muafiyetler: ledger tool satırları (Σ denetim izi), skill gövdeleri,
|
|
135
|
+
# goal-pinned birimler, user/assistant/system satırları (kural gereği
|
|
136
|
+
# zaten aday değil), hâlihazırda tombstone taşıyanlar (idempotence).
|
|
137
|
+
def tombstone!(row, rev_now)
|
|
138
|
+
return false unless Prompt::ConversationRows.row?(row)
|
|
139
|
+
|
|
140
|
+
role = Prompt::ConversationRows.role_of(row)
|
|
141
|
+
return false unless ["tool", "tool_result"].include?(role)
|
|
142
|
+
|
|
143
|
+
name = tool_name_of(row)
|
|
144
|
+
return false if name == ObservationProjection::MODULE_NAME
|
|
145
|
+
return false if name == ObservationProjection::SKILL_TOOL_NAME
|
|
146
|
+
return false if row[:status].to_s == "error" || row["status"].to_s == "error"
|
|
147
|
+
|
|
148
|
+
key = Prompt::ConversationRows.content_key_of(row)
|
|
149
|
+
content = row[key].to_s
|
|
150
|
+
return false if content.start_with?("<absorbed_into_ledger")
|
|
151
|
+
|
|
152
|
+
row[key] = %(<absorbed_into_ledger rev="#{rev_now}" tool="#{Rubik::Platform.xml_escape(name.to_s)}" />)
|
|
153
|
+
true
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def tool_name_of(row)
|
|
157
|
+
(row[:tool_name] || row["tool_name"]).to_s
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Sessizlik sözleşmesi: tek gözlemlenebilirlik on_step
|
|
161
|
+
# (:ledger_absorb_drop — StepFormatter bilinmeyen tipte nil döner →
|
|
162
|
+
# UI sessiz) + dmesg ring kaydı. Notice/event_bus/wake YOK.
|
|
163
|
+
def observe(count, rev_now, reclaimed)
|
|
164
|
+
@deps.on_step&.call(type: :ledger_absorb_drop, count: count, rev: rev_now,
|
|
165
|
+
reclaimed_tokens: reclaimed,
|
|
166
|
+
step: @deps.respond_to?(:step_count) ? @deps.step_count : nil,
|
|
167
|
+
max_steps: @deps.respond_to?(:max_steps) ? @deps.max_steps : nil)
|
|
168
|
+
dmesg = Rubik::Coeffects.get(@deps.runtime_manager&.context, Rubik::Coeffects::DMESG)
|
|
169
|
+
if dmesg.respond_to?(:log)
|
|
170
|
+
dmesg.log(:orchestrator, :info,
|
|
171
|
+
"goal ledger absorbed #{count} observation row(s) (rev=#{rev_now}, ~#{reclaimed} tokens reclaimed)")
|
|
172
|
+
end
|
|
173
|
+
rescue StandardError => e
|
|
174
|
+
Rubik::Log.warn(:orchestrator, "observation projection observability failed (#{e.class}: #{e.message})")
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|