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.
Files changed (523) hide show
  1. checksums.yaml +7 -0
  2. data/ATTRIBUTION.md +139 -0
  3. data/CHANGELOG.md +236 -0
  4. data/LICENSE +21 -0
  5. data/README.md +269 -0
  6. data/bin/rubik +82 -0
  7. data/lib/rubik/adapter/base.rb +195 -0
  8. data/lib/rubik/adapter/c.rb +36 -0
  9. data/lib/rubik/adapter/crystal.rb +78 -0
  10. data/lib/rubik/adapter/generic.rb +86 -0
  11. data/lib/rubik/adapter/go.rb +36 -0
  12. data/lib/rubik/adapter/node.rb +146 -0
  13. data/lib/rubik/adapter/process.rb +78 -0
  14. data/lib/rubik/adapter/python.rb +30 -0
  15. data/lib/rubik/adapter/registry.rb +81 -0
  16. data/lib/rubik/adapter/ruby.rb +30 -0
  17. data/lib/rubik/adapter/rust.rb +36 -0
  18. data/lib/rubik/adapter/zig.rb +36 -0
  19. data/lib/rubik/adapter.rb +18 -0
  20. data/lib/rubik/ast/body_loader.rb +19 -0
  21. data/lib/rubik/ast/c_extractor.rb +114 -0
  22. data/lib/rubik/ast/cache_store.rb +191 -0
  23. data/lib/rubik/ast/extractor.rb +59 -0
  24. data/lib/rubik/ast/fallback_cleaner.rb +145 -0
  25. data/lib/rubik/ast/go_extractor.rb +148 -0
  26. data/lib/rubik/ast/graph.rb +272 -0
  27. data/lib/rubik/ast/python_extractor.rb +120 -0
  28. data/lib/rubik/ast/ruby_extractor.rb +260 -0
  29. data/lib/rubik/ast/rust_extractor.rb +127 -0
  30. data/lib/rubik/ast/symbol.rb +103 -0
  31. data/lib/rubik/ast/treesitter/base_walker.rb +109 -0
  32. data/lib/rubik/ast/treesitter/c_walker.rb +122 -0
  33. data/lib/rubik/ast/treesitter/go_walker.rb +120 -0
  34. data/lib/rubik/ast/treesitter/python_walker.rb +107 -0
  35. data/lib/rubik/ast/treesitter/rust_walker.rb +130 -0
  36. data/lib/rubik/ast/treesitter/typescript_walker.rb +182 -0
  37. data/lib/rubik/ast/treesitter/zig_walker.rb +109 -0
  38. data/lib/rubik/ast/treesitter_extractor.rb +93 -0
  39. data/lib/rubik/ast/typescript_extractor.rb +283 -0
  40. data/lib/rubik/ast/zig_extractor.rb +116 -0
  41. data/lib/rubik/ast.rb +13 -0
  42. data/lib/rubik/benchmark/runner.rb +224 -0
  43. data/lib/rubik/benchmark/synth_repo.rb +59 -0
  44. data/lib/rubik/benchmark.rb +9 -0
  45. data/lib/rubik/buffer/diff.rb +43 -0
  46. data/lib/rubik/buffer/manager.rb +427 -0
  47. data/lib/rubik/buffer/path_validator.rb +60 -0
  48. data/lib/rubik/buffer/snapshot.rb +99 -0
  49. data/lib/rubik/buffer.rb +10 -0
  50. data/lib/rubik/coeffects.rb +129 -0
  51. data/lib/rubik/concurrency/boundary.rb +74 -0
  52. data/lib/rubik/concurrency/dns_pool.rb +68 -0
  53. data/lib/rubik/concurrency/errors.rb +17 -0
  54. data/lib/rubik/concurrency/event_loop.rb +441 -0
  55. data/lib/rubik/concurrency/join.rb +55 -0
  56. data/lib/rubik/concurrency/process_waiter.rb +55 -0
  57. data/lib/rubik/concurrency/scheduler.rb +84 -0
  58. data/lib/rubik/concurrency/timers.rb +109 -0
  59. data/lib/rubik/concurrency.rb +132 -0
  60. data/lib/rubik/config/deep_ops.rb +80 -0
  61. data/lib/rubik/config/defaults.rb +248 -0
  62. data/lib/rubik/config/model_catalog.rb +93 -0
  63. data/lib/rubik/config/store.rb +155 -0
  64. data/lib/rubik/config.rb +216 -0
  65. data/lib/rubik/context/git_info.rb +84 -0
  66. data/lib/rubik/context/git_lister.rb +37 -0
  67. data/lib/rubik/context/index.rb +134 -0
  68. data/lib/rubik/context/repo_context_reader.rb +121 -0
  69. data/lib/rubik/context/scanner.rb +439 -0
  70. data/lib/rubik/context.rb +11 -0
  71. data/lib/rubik/diagnostics/collector.rb +200 -0
  72. data/lib/rubik/diagnostics/entry.rb +60 -0
  73. data/lib/rubik/diagnostics/parser.rb +107 -0
  74. data/lib/rubik/diagnostics.rb +10 -0
  75. data/lib/rubik/event_bus.rb +56 -0
  76. data/lib/rubik/kernel/dispatcher.rb +171 -0
  77. data/lib/rubik/kernel/dmesg.rb +116 -0
  78. data/lib/rubik/kernel/init.rb +177 -0
  79. data/lib/rubik/kernel/record.rb +85 -0
  80. data/lib/rubik/kernel/unit.rb +160 -0
  81. data/lib/rubik/llm/anthropic.rb +202 -0
  82. data/lib/rubik/llm/chunk_assembler.rb +285 -0
  83. data/lib/rubik/llm/client.rb +526 -0
  84. data/lib/rubik/llm/formatter.rb +450 -0
  85. data/lib/rubik/llm/message_sanitizer.rb +87 -0
  86. data/lib/rubik/llm/ollama.rb +167 -0
  87. data/lib/rubik/llm/openai.rb +21 -0
  88. data/lib/rubik/llm/openai_compatible.rb +216 -0
  89. data/lib/rubik/llm/provider.rb +125 -0
  90. data/lib/rubik/llm/registry.rb +336 -0
  91. data/lib/rubik/llm/router.rb +100 -0
  92. data/lib/rubik/llm.rb +18 -0
  93. data/lib/rubik/log.rb +38 -0
  94. data/lib/rubik/lsp/client.rb +475 -0
  95. data/lib/rubik/lsp/manager.rb +366 -0
  96. data/lib/rubik/lsp.rb +9 -0
  97. data/lib/rubik/mcp/boot_lifecycle.rb +114 -0
  98. data/lib/rubik/mcp/client.rb +169 -0
  99. data/lib/rubik/mcp/errors.rb +11 -0
  100. data/lib/rubik/mcp/manager.rb +619 -0
  101. data/lib/rubik/mcp/proxy_tool.rb +104 -0
  102. data/lib/rubik/mcp/transport/stdio.rb +255 -0
  103. data/lib/rubik/mcp.rb +12 -0
  104. data/lib/rubik/orchestrator/activity_log.rb +75 -0
  105. data/lib/rubik/orchestrator/auto_apply.rb +166 -0
  106. data/lib/rubik/orchestrator/budget_manager.rb +97 -0
  107. data/lib/rubik/orchestrator/continuation_manager.rb +161 -0
  108. data/lib/rubik/orchestrator/finish_message_selector.rb +55 -0
  109. data/lib/rubik/orchestrator/goal_ledger/drift.rb +71 -0
  110. data/lib/rubik/orchestrator/goal_ledger/ledger.rb +399 -0
  111. data/lib/rubik/orchestrator/goal_ledger/observation_projection.rb +179 -0
  112. data/lib/rubik/orchestrator/goal_ledger/patch.rb +268 -0
  113. data/lib/rubik/orchestrator/goal_ledger/schema.rb +123 -0
  114. data/lib/rubik/orchestrator/goal_ledger/validator.rb +361 -0
  115. data/lib/rubik/orchestrator/goal_ledger/verifier.rb +129 -0
  116. data/lib/rubik/orchestrator/goal_ledger/verify_spec.rb +50 -0
  117. data/lib/rubik/orchestrator/goal_ledger.rb +65 -0
  118. data/lib/rubik/orchestrator/goal_state.rb +184 -0
  119. data/lib/rubik/orchestrator/goal_wait.rb +125 -0
  120. data/lib/rubik/orchestrator/loop.rb +388 -0
  121. data/lib/rubik/orchestrator/lsp_feedback.rb +55 -0
  122. data/lib/rubik/orchestrator/maestro_gate.rb +65 -0
  123. data/lib/rubik/orchestrator/provider_recovery.rb +90 -0
  124. data/lib/rubik/orchestrator/provider_resolver.rb +69 -0
  125. data/lib/rubik/orchestrator/shell_deps.rb +85 -0
  126. data/lib/rubik/orchestrator/step_executor.rb +328 -0
  127. data/lib/rubik/orchestrator/stream_manager.rb +118 -0
  128. data/lib/rubik/orchestrator/subagent/activation.rb +65 -0
  129. data/lib/rubik/orchestrator/subagent/budget.rb +108 -0
  130. data/lib/rubik/orchestrator/subagent/completion.rb +61 -0
  131. data/lib/rubik/orchestrator/subagent/concurrency_gate.rb +37 -0
  132. data/lib/rubik/orchestrator/subagent/conversation_id.rb +67 -0
  133. data/lib/rubik/orchestrator/subagent/dependency.rb +398 -0
  134. data/lib/rubik/orchestrator/subagent/instance.rb +384 -0
  135. data/lib/rubik/orchestrator/subagent/lifecycle.rb +118 -0
  136. data/lib/rubik/orchestrator/subagent/messaging.rb +61 -0
  137. data/lib/rubik/orchestrator/subagent/narration.rb +125 -0
  138. data/lib/rubik/orchestrator/subagent/notices.rb +209 -0
  139. data/lib/rubik/orchestrator/subagent/questioner.rb +80 -0
  140. data/lib/rubik/orchestrator/subagent/reaper.rb +98 -0
  141. data/lib/rubik/orchestrator/subagent/resilience.rb +70 -0
  142. data/lib/rubik/orchestrator/subagent/reuse.rb +127 -0
  143. data/lib/rubik/orchestrator/subagent/scoped_registry.rb +79 -0
  144. data/lib/rubik/orchestrator/subagent/step_runner.rb +240 -0
  145. data/lib/rubik/orchestrator/subagent/storage_id.rb +26 -0
  146. data/lib/rubik/orchestrator/subagent/supervisor.rb +327 -0
  147. data/lib/rubik/orchestrator/subagent/transcript_formatter.rb +141 -0
  148. data/lib/rubik/orchestrator/subagent/type_registry.rb +199 -0
  149. data/lib/rubik/orchestrator/subagent_notice_inbox.rb +80 -0
  150. data/lib/rubik/orchestrator/tool_call_id.rb +47 -0
  151. data/lib/rubik/orchestrator/tool_circuit_breaker.rb +91 -0
  152. data/lib/rubik/orchestrator/tool_history.rb +39 -0
  153. data/lib/rubik/orchestrator/turn_executor/subagent_channel.rb +102 -0
  154. data/lib/rubik/orchestrator/turn_executor/user_channel.rb +172 -0
  155. data/lib/rubik/orchestrator/turn_executor.rb +376 -0
  156. data/lib/rubik/orchestrator.rb +11 -0
  157. data/lib/rubik/platform/filesystem.rb +64 -0
  158. data/lib/rubik/platform/io_drain.rb +53 -0
  159. data/lib/rubik/platform/path.rb +210 -0
  160. data/lib/rubik/platform/process.rb +38 -0
  161. data/lib/rubik/platform/sanitize.rb +157 -0
  162. data/lib/rubik/platform.rb +113 -0
  163. data/lib/rubik/plugin/dsl.rb +51 -0
  164. data/lib/rubik/plugin/manager.rb +247 -0
  165. data/lib/rubik/plugin/storage.rb +195 -0
  166. data/lib/rubik/plugin.rb +10 -0
  167. data/lib/rubik/prompt/context_builder.rb +377 -0
  168. data/lib/rubik/prompt/context_compactor.rb +326 -0
  169. data/lib/rubik/prompt/conversation_rows.rb +74 -0
  170. data/lib/rubik/prompt/doctrine.rb +160 -0
  171. data/lib/rubik/prompt/emergency_compactor.rb +81 -0
  172. data/lib/rubik/prompt/services.rb +60 -0
  173. data/lib/rubik/prompt/token_estimator.rb +63 -0
  174. data/lib/rubik/prompt/tool_teachings.rb +78 -0
  175. data/lib/rubik/prompt.rb +13 -0
  176. data/lib/rubik/repair/loop.rb +95 -0
  177. data/lib/rubik/repair.rb +8 -0
  178. data/lib/rubik/repl/ast_warmup.rb +129 -0
  179. data/lib/rubik/repl/command_context.rb +214 -0
  180. data/lib/rubik/repl/command_parser.rb +48 -0
  181. data/lib/rubik/repl/commands/agent.rb +30 -0
  182. data/lib/rubik/repl/commands/agent_context.rb +289 -0
  183. data/lib/rubik/repl/commands/agent_execution.rb +342 -0
  184. data/lib/rubik/repl/commands/agent_models.rb +212 -0
  185. data/lib/rubik/repl/commands/agent_providers.rb +232 -0
  186. data/lib/rubik/repl/commands/base.rb +55 -0
  187. data/lib/rubik/repl/commands/buffer.rb +124 -0
  188. data/lib/rubik/repl/commands/dmesg.rb +87 -0
  189. data/lib/rubik/repl/commands/lsp.rb +235 -0
  190. data/lib/rubik/repl/commands/mcp.rb +194 -0
  191. data/lib/rubik/repl/commands/plugins.rb +208 -0
  192. data/lib/rubik/repl/commands/pruner.rb +71 -0
  193. data/lib/rubik/repl/commands/session.rb +183 -0
  194. data/lib/rubik/repl/commands/skills.rb +111 -0
  195. data/lib/rubik/repl/commands/storage.rb +85 -0
  196. data/lib/rubik/repl/commands/subagents.rb +195 -0
  197. data/lib/rubik/repl/commands/system.rb +85 -0
  198. data/lib/rubik/repl/commands/tasks.rb +120 -0
  199. data/lib/rubik/repl/commands/workspace.rb +443 -0
  200. data/lib/rubik/repl/commands.rb +44 -0
  201. data/lib/rubik/repl/composition.rb +32 -0
  202. data/lib/rubik/repl/config_wiring.rb +142 -0
  203. data/lib/rubik/repl/confirmations.rb +56 -0
  204. data/lib/rubik/repl/hooks.rb +47 -0
  205. data/lib/rubik/repl/input_execution.rb +323 -0
  206. data/lib/rubik/repl/input_loop.rb +213 -0
  207. data/lib/rubik/repl/interrupt_handler.rb +217 -0
  208. data/lib/rubik/repl/lifecycle.rb +410 -0
  209. data/lib/rubik/repl/paste_support.rb +88 -0
  210. data/lib/rubik/repl/pending_notice_queue.rb +60 -0
  211. data/lib/rubik/repl/presentation.rb +132 -0
  212. data/lib/rubik/repl/pruner/brief.rb +101 -0
  213. data/lib/rubik/repl/pruner/gate.rb +279 -0
  214. data/lib/rubik/repl/pruner/harness.rb +236 -0
  215. data/lib/rubik/repl/pruner/rules.rb +316 -0
  216. data/lib/rubik/repl/pruner/service.rb +82 -0
  217. data/lib/rubik/repl/pruner.rb +39 -0
  218. data/lib/rubik/repl/service_catalog.rb +374 -0
  219. data/lib/rubik/repl/setup_factories.rb +66 -0
  220. data/lib/rubik/repl/shell.rb +231 -0
  221. data/lib/rubik/repl/step_formatter.rb +324 -0
  222. data/lib/rubik/repl/tee_io.rb +105 -0
  223. data/lib/rubik/repl/terminal_guard.rb +157 -0
  224. data/lib/rubik/repl/turn_recorder.rb +234 -0
  225. data/lib/rubik/repl.rb +14 -0
  226. data/lib/rubik/runtime/ast_service.rb +78 -0
  227. data/lib/rubik/runtime/context.rb +335 -0
  228. data/lib/rubik/runtime/diagnostic_engine.rb +188 -0
  229. data/lib/rubik/runtime/discovery.rb +342 -0
  230. data/lib/rubik/runtime/fiber.rb +142 -0
  231. data/lib/rubik/runtime/fiber_ordering.rb +52 -0
  232. data/lib/rubik/runtime/fiber_retry_gate.rb +84 -0
  233. data/lib/rubik/runtime/index_service.rb +58 -0
  234. data/lib/rubik/runtime/loop_service.rb +73 -0
  235. data/lib/rubik/runtime/manager.rb +241 -0
  236. data/lib/rubik/runtime/overlay_storage.rb +481 -0
  237. data/lib/rubik/runtime/service_definition.rb +54 -0
  238. data/lib/rubik/runtime/service_registration.rb +33 -0
  239. data/lib/rubik/runtime/task_manager.rb +302 -0
  240. data/lib/rubik/runtime.rb +19 -0
  241. data/lib/rubik/sandbox/driver/base.rb +30 -0
  242. data/lib/rubik/sandbox/driver/bubblewrap.rb +111 -0
  243. data/lib/rubik/sandbox/driver/docker.rb +113 -0
  244. data/lib/rubik/sandbox/driver/podman.rb +98 -0
  245. data/lib/rubik/sandbox/driver/process.rb +190 -0
  246. data/lib/rubik/sandbox/driver.rb +91 -0
  247. data/lib/rubik/sandbox/runner.rb +218 -0
  248. data/lib/rubik/sandbox/shell_resolver.rb +178 -0
  249. data/lib/rubik/sandbox.rb +8 -0
  250. data/lib/rubik/security/blocked_guidance.rb +187 -0
  251. data/lib/rubik/security/classifier/classifier.rb +440 -0
  252. data/lib/rubik/security/classifier/commands/agent-browser.yaml +9 -0
  253. data/lib/rubik/security/classifier/commands/apt.yaml +25 -0
  254. data/lib/rubik/security/classifier/commands/awk.yaml +10 -0
  255. data/lib/rubik/security/classifier/commands/basename.yaml +7 -0
  256. data/lib/rubik/security/classifier/commands/bash-classify.yaml +8 -0
  257. data/lib/rubik/security/classifier/commands/bash.yaml +20 -0
  258. data/lib/rubik/security/classifier/commands/black.yaml +12 -0
  259. data/lib/rubik/security/classifier/commands/brew.yaml +31 -0
  260. data/lib/rubik/security/classifier/commands/builtin.yaml +8 -0
  261. data/lib/rubik/security/classifier/commands/cargo.yaml +114 -0
  262. data/lib/rubik/security/classifier/commands/cat.yaml +7 -0
  263. data/lib/rubik/security/classifier/commands/chgrp.yaml +10 -0
  264. data/lib/rubik/security/classifier/commands/chmod.yaml +10 -0
  265. data/lib/rubik/security/classifier/commands/chown.yaml +10 -0
  266. data/lib/rubik/security/classifier/commands/column.yaml +7 -0
  267. data/lib/rubik/security/classifier/commands/comm.yaml +7 -0
  268. data/lib/rubik/security/classifier/commands/command.yaml +12 -0
  269. data/lib/rubik/security/classifier/commands/cp.yaml +7 -0
  270. data/lib/rubik/security/classifier/commands/crystal.yaml +63 -0
  271. data/lib/rubik/security/classifier/commands/curl.yaml +30 -0
  272. data/lib/rubik/security/classifier/commands/cut.yaml +7 -0
  273. data/lib/rubik/security/classifier/commands/date.yaml +10 -0
  274. data/lib/rubik/security/classifier/commands/df.yaml +7 -0
  275. data/lib/rubik/security/classifier/commands/diff.yaml +7 -0
  276. data/lib/rubik/security/classifier/commands/dig.yaml +7 -0
  277. data/lib/rubik/security/classifier/commands/dirname.yaml +7 -0
  278. data/lib/rubik/security/classifier/commands/docker.yaml +177 -0
  279. data/lib/rubik/security/classifier/commands/du.yaml +7 -0
  280. data/lib/rubik/security/classifier/commands/echo.yaml +7 -0
  281. data/lib/rubik/security/classifier/commands/env.yaml +15 -0
  282. data/lib/rubik/security/classifier/commands/eslint.yaml +12 -0
  283. data/lib/rubik/security/classifier/commands/eval.yaml +8 -0
  284. data/lib/rubik/security/classifier/commands/exec.yaml +12 -0
  285. data/lib/rubik/security/classifier/commands/expand.yaml +7 -0
  286. data/lib/rubik/security/classifier/commands/false.yaml +7 -0
  287. data/lib/rubik/security/classifier/commands/file.yaml +7 -0
  288. data/lib/rubik/security/classifier/commands/find.yaml +25 -0
  289. data/lib/rubik/security/classifier/commands/fmt.yaml +7 -0
  290. data/lib/rubik/security/classifier/commands/fold.yaml +7 -0
  291. data/lib/rubik/security/classifier/commands/free.yaml +7 -0
  292. data/lib/rubik/security/classifier/commands/gawk.yaml +10 -0
  293. data/lib/rubik/security/classifier/commands/gcloud.yaml +979 -0
  294. data/lib/rubik/security/classifier/commands/gh.yaml +615 -0
  295. data/lib/rubik/security/classifier/commands/git.yaml +524 -0
  296. data/lib/rubik/security/classifier/commands/glab-discussion.yaml +21 -0
  297. data/lib/rubik/security/classifier/commands/glab.yaml +807 -0
  298. data/lib/rubik/security/classifier/commands/go.yaml +44 -0
  299. data/lib/rubik/security/classifier/commands/gog.yaml +983 -0
  300. data/lib/rubik/security/classifier/commands/gradle.yaml +92 -0
  301. data/lib/rubik/security/classifier/commands/gradlew.yaml +92 -0
  302. data/lib/rubik/security/classifier/commands/grep.yaml +7 -0
  303. data/lib/rubik/security/classifier/commands/groups.yaml +7 -0
  304. data/lib/rubik/security/classifier/commands/gunzip.yaml +8 -0
  305. data/lib/rubik/security/classifier/commands/gws.yaml +211 -0
  306. data/lib/rubik/security/classifier/commands/gzip.yaml +7 -0
  307. data/lib/rubik/security/classifier/commands/hadolint.yaml +9 -0
  308. data/lib/rubik/security/classifier/commands/halt.yaml +8 -0
  309. data/lib/rubik/security/classifier/commands/head.yaml +7 -0
  310. data/lib/rubik/security/classifier/commands/helm.yaml +129 -0
  311. data/lib/rubik/security/classifier/commands/hostname.yaml +7 -0
  312. data/lib/rubik/security/classifier/commands/id.yaml +7 -0
  313. data/lib/rubik/security/classifier/commands/install.yaml +7 -0
  314. data/lib/rubik/security/classifier/commands/isort.yaml +14 -0
  315. data/lib/rubik/security/classifier/commands/java.yaml +35 -0
  316. data/lib/rubik/security/classifier/commands/javac.yaml +39 -0
  317. data/lib/rubik/security/classifier/commands/join.yaml +7 -0
  318. data/lib/rubik/security/classifier/commands/journalctl.yaml +14 -0
  319. data/lib/rubik/security/classifier/commands/jq.yaml +7 -0
  320. data/lib/rubik/security/classifier/commands/kill.yaml +7 -0
  321. data/lib/rubik/security/classifier/commands/killall.yaml +7 -0
  322. data/lib/rubik/security/classifier/commands/kubectl.yaml +211 -0
  323. data/lib/rubik/security/classifier/commands/less.yaml +7 -0
  324. data/lib/rubik/security/classifier/commands/ln.yaml +7 -0
  325. data/lib/rubik/security/classifier/commands/ls.yaml +7 -0
  326. data/lib/rubik/security/classifier/commands/make.yaml +21 -0
  327. data/lib/rubik/security/classifier/commands/md5sum.yaml +7 -0
  328. data/lib/rubik/security/classifier/commands/mise.yaml +579 -0
  329. data/lib/rubik/security/classifier/commands/mkdir.yaml +8 -0
  330. data/lib/rubik/security/classifier/commands/more.yaml +7 -0
  331. data/lib/rubik/security/classifier/commands/mv.yaml +7 -0
  332. data/lib/rubik/security/classifier/commands/mvn.yaml +83 -0
  333. data/lib/rubik/security/classifier/commands/mvnw.yaml +83 -0
  334. data/lib/rubik/security/classifier/commands/mypy.yaml +9 -0
  335. data/lib/rubik/security/classifier/commands/netstat.yaml +7 -0
  336. data/lib/rubik/security/classifier/commands/nice.yaml +11 -0
  337. data/lib/rubik/security/classifier/commands/nl.yaml +7 -0
  338. data/lib/rubik/security/classifier/commands/node.yaml +20 -0
  339. data/lib/rubik/security/classifier/commands/nohup.yaml +9 -0
  340. data/lib/rubik/security/classifier/commands/npm.yaml +71 -0
  341. data/lib/rubik/security/classifier/commands/npx.yaml +42 -0
  342. data/lib/rubik/security/classifier/commands/nslookup.yaml +7 -0
  343. data/lib/rubik/security/classifier/commands/paste.yaml +7 -0
  344. data/lib/rubik/security/classifier/commands/ping.yaml +7 -0
  345. data/lib/rubik/security/classifier/commands/pip.yaml +33 -0
  346. data/lib/rubik/security/classifier/commands/pip3.yaml +33 -0
  347. data/lib/rubik/security/classifier/commands/pipx.yaml +33 -0
  348. data/lib/rubik/security/classifier/commands/pkill.yaml +7 -0
  349. data/lib/rubik/security/classifier/commands/pnpm.yaml +346 -0
  350. data/lib/rubik/security/classifier/commands/poetry.yaml +97 -0
  351. data/lib/rubik/security/classifier/commands/poweroff.yaml +8 -0
  352. data/lib/rubik/security/classifier/commands/prettier.yaml +13 -0
  353. data/lib/rubik/security/classifier/commands/printenv.yaml +7 -0
  354. data/lib/rubik/security/classifier/commands/printf.yaml +7 -0
  355. data/lib/rubik/security/classifier/commands/ps.yaml +7 -0
  356. data/lib/rubik/security/classifier/commands/pup.yaml +882 -0
  357. data/lib/rubik/security/classifier/commands/pwd.yaml +7 -0
  358. data/lib/rubik/security/classifier/commands/pytest.yaml +7 -0
  359. data/lib/rubik/security/classifier/commands/python.yaml +9 -0
  360. data/lib/rubik/security/classifier/commands/python3.yaml +9 -0
  361. data/lib/rubik/security/classifier/commands/read.yaml +7 -0
  362. data/lib/rubik/security/classifier/commands/readlink.yaml +7 -0
  363. data/lib/rubik/security/classifier/commands/realpath.yaml +7 -0
  364. data/lib/rubik/security/classifier/commands/reboot.yaml +8 -0
  365. data/lib/rubik/security/classifier/commands/rev.yaml +7 -0
  366. data/lib/rubik/security/classifier/commands/rg.yaml +7 -0
  367. data/lib/rubik/security/classifier/commands/rm.yaml +8 -0
  368. data/lib/rubik/security/classifier/commands/rsync.yaml +12 -0
  369. data/lib/rubik/security/classifier/commands/rtk.yaml +140 -0
  370. data/lib/rubik/security/classifier/commands/ruff.yaml +29 -0
  371. data/lib/rubik/security/classifier/commands/rustfmt.yaml +14 -0
  372. data/lib/rubik/security/classifier/commands/scp.yaml +7 -0
  373. data/lib/rubik/security/classifier/commands/sed.yaml +14 -0
  374. data/lib/rubik/security/classifier/commands/seq.yaml +7 -0
  375. data/lib/rubik/security/classifier/commands/sh.yaml +14 -0
  376. data/lib/rubik/security/classifier/commands/sha1sum.yaml +7 -0
  377. data/lib/rubik/security/classifier/commands/sha256sum.yaml +7 -0
  378. data/lib/rubik/security/classifier/commands/shellcheck.yaml +10 -0
  379. data/lib/rubik/security/classifier/commands/shutdown.yaml +8 -0
  380. data/lib/rubik/security/classifier/commands/slack.yaml +113 -0
  381. data/lib/rubik/security/classifier/commands/sleep.yaml +8 -0
  382. data/lib/rubik/security/classifier/commands/snip.yaml +79 -0
  383. data/lib/rubik/security/classifier/commands/sort.yaml +10 -0
  384. data/lib/rubik/security/classifier/commands/ss.yaml +7 -0
  385. data/lib/rubik/security/classifier/commands/ssh.yaml +41 -0
  386. data/lib/rubik/security/classifier/commands/stat.yaml +7 -0
  387. data/lib/rubik/security/classifier/commands/stdbuf.yaml +15 -0
  388. data/lib/rubik/security/classifier/commands/strace.yaml +20 -0
  389. data/lib/rubik/security/classifier/commands/strings.yaml +11 -0
  390. data/lib/rubik/security/classifier/commands/sudo.yaml +24 -0
  391. data/lib/rubik/security/classifier/commands/systemctl.yaml +58 -0
  392. data/lib/rubik/security/classifier/commands/systemd-run.yaml +30 -0
  393. data/lib/rubik/security/classifier/commands/tac.yaml +7 -0
  394. data/lib/rubik/security/classifier/commands/tail.yaml +7 -0
  395. data/lib/rubik/security/classifier/commands/tar.yaml +25 -0
  396. data/lib/rubik/security/classifier/commands/tee.yaml +8 -0
  397. data/lib/rubik/security/classifier/commands/terraform.yaml +70 -0
  398. data/lib/rubik/security/classifier/commands/test.yaml +7 -0
  399. data/lib/rubik/security/classifier/commands/time.yaml +8 -0
  400. data/lib/rubik/security/classifier/commands/timeout.yaml +18 -0
  401. data/lib/rubik/security/classifier/commands/top.yaml +7 -0
  402. data/lib/rubik/security/classifier/commands/touch.yaml +8 -0
  403. data/lib/rubik/security/classifier/commands/tr.yaml +7 -0
  404. data/lib/rubik/security/classifier/commands/traceroute.yaml +7 -0
  405. data/lib/rubik/security/classifier/commands/true.yaml +7 -0
  406. data/lib/rubik/security/classifier/commands/tsc.yaml +16 -0
  407. data/lib/rubik/security/classifier/commands/type.yaml +7 -0
  408. data/lib/rubik/security/classifier/commands/uname.yaml +7 -0
  409. data/lib/rubik/security/classifier/commands/unexpand.yaml +7 -0
  410. data/lib/rubik/security/classifier/commands/uniq.yaml +7 -0
  411. data/lib/rubik/security/classifier/commands/unzip.yaml +10 -0
  412. data/lib/rubik/security/classifier/commands/uptime.yaml +7 -0
  413. data/lib/rubik/security/classifier/commands/uv.yaml +156 -0
  414. data/lib/rubik/security/classifier/commands/wait.yaml +11 -0
  415. data/lib/rubik/security/classifier/commands/wc.yaml +7 -0
  416. data/lib/rubik/security/classifier/commands/wget.yaml +7 -0
  417. data/lib/rubik/security/classifier/commands/whereis.yaml +7 -0
  418. data/lib/rubik/security/classifier/commands/which.yaml +7 -0
  419. data/lib/rubik/security/classifier/commands/whoami.yaml +7 -0
  420. data/lib/rubik/security/classifier/commands/xargs.yaml +26 -0
  421. data/lib/rubik/security/classifier/commands/xmllint.yaml +7 -0
  422. data/lib/rubik/security/classifier/commands/yarn.yaml +72 -0
  423. data/lib/rubik/security/classifier/commands/yes.yaml +7 -0
  424. data/lib/rubik/security/classifier/commands/yq.yaml +10 -0
  425. data/lib/rubik/security/classifier/commands/zip.yaml +8 -0
  426. data/lib/rubik/security/classifier/commands/zsh.yaml +14 -0
  427. data/lib/rubik/security/classifier/database.rb +216 -0
  428. data/lib/rubik/security/classifier/matcher/delegation.rb +190 -0
  429. data/lib/rubik/security/classifier/matcher/options.rb +245 -0
  430. data/lib/rubik/security/classifier/matcher/subcommands.rb +103 -0
  431. data/lib/rubik/security/classifier/matcher.rb +249 -0
  432. data/lib/rubik/security/classifier/models.rb +170 -0
  433. data/lib/rubik/security/classifier/parser.rb +23 -0
  434. data/lib/rubik/security/classifier/parsers/bash.rb +280 -0
  435. data/lib/rubik/security/classifier/parsers/bash_extractors.rb +221 -0
  436. data/lib/rubik/security/classifier/parsers/powershell.rb +185 -0
  437. data/lib/rubik/security/classifier/pwsh_rules.rb +224 -0
  438. data/lib/rubik/security/classifier.rb +25 -0
  439. data/lib/rubik/security/errors.rb +39 -0
  440. data/lib/rubik/security/guard.rb +954 -0
  441. data/lib/rubik/security/redactor.rb +89 -0
  442. data/lib/rubik/security.rb +11 -0
  443. data/lib/rubik/session/manager.rb +331 -0
  444. data/lib/rubik/session/recorder.rb +149 -0
  445. data/lib/rubik/session/replayer.rb +65 -0
  446. data/lib/rubik/session.rb +10 -0
  447. data/lib/rubik/skills/frontmatter.rb +41 -0
  448. data/lib/rubik/skills/manager.rb +223 -0
  449. data/lib/rubik/skills/registry.rb +64 -0
  450. data/lib/rubik/skills/skill.rb +33 -0
  451. data/lib/rubik/skills/storage.rb +77 -0
  452. data/lib/rubik/skills/validator.rb +116 -0
  453. data/lib/rubik/skills.rb +20 -0
  454. data/lib/rubik/storage/folding.rb +23 -0
  455. data/lib/rubik/storage/layout.rb +145 -0
  456. data/lib/rubik/storage/render.rb +97 -0
  457. data/lib/rubik/storage/service.rb +42 -0
  458. data/lib/rubik/storage/store.rb +514 -0
  459. data/lib/rubik/storage/transcript_indexer.rb +116 -0
  460. data/lib/rubik/storage/uri_reader.rb +168 -0
  461. data/lib/rubik/storage/vakanuvis/annal_writer.rb +92 -0
  462. data/lib/rubik/storage/vakanuvis/distill_gate.rb +323 -0
  463. data/lib/rubik/storage/vakanuvis/gate.rb +250 -0
  464. data/lib/rubik/storage/vakanuvis/harness.rb +421 -0
  465. data/lib/rubik/storage/vakanuvis/memory_steward.rb +163 -0
  466. data/lib/rubik/storage/vakanuvis/service.rb +69 -0
  467. data/lib/rubik/storage/vakanuvis/vocabulary.rb +99 -0
  468. data/lib/rubik/storage/vakanuvis/wiki_skeleton.rb +67 -0
  469. data/lib/rubik/storage/vakanuvis.rb +77 -0
  470. data/lib/rubik/storage.rb +29 -0
  471. data/lib/rubik/tools/base.rb +107 -0
  472. data/lib/rubik/tools/builtins/dmesg.rb +74 -0
  473. data/lib/rubik/tools/builtins/exec.rb +293 -0
  474. data/lib/rubik/tools/builtins/files.rb +354 -0
  475. data/lib/rubik/tools/builtins/goal_ledger.rb +89 -0
  476. data/lib/rubik/tools/builtins/grep.rb +281 -0
  477. data/lib/rubik/tools/builtins/interactive.rb +175 -0
  478. data/lib/rubik/tools/builtins/lifecycle.rb +359 -0
  479. data/lib/rubik/tools/builtins/lsp.rb +283 -0
  480. data/lib/rubik/tools/builtins/pruner.rb +88 -0
  481. data/lib/rubik/tools/builtins/runtime.rb +120 -0
  482. data/lib/rubik/tools/builtins/send_message.rb +109 -0
  483. data/lib/rubik/tools/builtins/show_file.rb +84 -0
  484. data/lib/rubik/tools/builtins/skill.rb +72 -0
  485. data/lib/rubik/tools/builtins/storage.rb +232 -0
  486. data/lib/rubik/tools/builtins/subagent.rb +356 -0
  487. data/lib/rubik/tools/builtins/subagent_lifecycle.rb +72 -0
  488. data/lib/rubik/tools/builtins/symbols.rb +209 -0
  489. data/lib/rubik/tools/builtins/synthesis.rb +314 -0
  490. data/lib/rubik/tools/builtins/task.rb +168 -0
  491. data/lib/rubik/tools/builtins/unsynthesize.rb +111 -0
  492. data/lib/rubik/tools/builtins/vakanuvis.rb +161 -0
  493. data/lib/rubik/tools/builtins.rb +30 -0
  494. data/lib/rubik/tools/catalog.rb +70 -0
  495. data/lib/rubik/tools/declaration.rb +90 -0
  496. data/lib/rubik/tools/diagnostic_reporter.rb +283 -0
  497. data/lib/rubik/tools/fuzzy_matcher.rb +172 -0
  498. data/lib/rubik/tools/native_search.rb +180 -0
  499. data/lib/rubik/tools/output.rb +54 -0
  500. data/lib/rubik/tools/preflight_runner.rb +209 -0
  501. data/lib/rubik/tools/registry.rb +247 -0
  502. data/lib/rubik/tools/result.rb +71 -0
  503. data/lib/rubik/tools/services.rb +96 -0
  504. data/lib/rubik/tools/truncator.rb +78 -0
  505. data/lib/rubik/tools/uri_resolver.rb +195 -0
  506. data/lib/rubik/tools.rb +14 -0
  507. data/lib/rubik/ui/approvals.rb +52 -0
  508. data/lib/rubik/ui/banner.rb +43 -0
  509. data/lib/rubik/ui/capability.rb +122 -0
  510. data/lib/rubik/ui/dialog.rb +106 -0
  511. data/lib/rubik/ui/diff_viewer.rb +78 -0
  512. data/lib/rubik/ui/frame.rb +95 -0
  513. data/lib/rubik/ui/markdown_stream.rb +107 -0
  514. data/lib/rubik/ui/panel.rb +98 -0
  515. data/lib/rubik/ui/renderer.rb +206 -0
  516. data/lib/rubik/ui/spinner.rb +120 -0
  517. data/lib/rubik/ui/statusline.rb +134 -0
  518. data/lib/rubik/ui/theme.rb +118 -0
  519. data/lib/rubik/ui.rb +57 -0
  520. data/lib/rubik/uris.rb +101 -0
  521. data/lib/rubik/version.rb +5 -0
  522. data/lib/rubik.rb +65 -0
  523. metadata +770 -0
@@ -0,0 +1,268 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Rubik
6
+ module Orchestrator
7
+ module GoalLedger
8
+ # ΔΣ patch'i için immutable value object — RFC 6902 alt kümesi
9
+ # (add/replace/remove; move/copy/test bilinçli dışarıda). Parse ASLA
10
+ # raise etmez: her başarısızlık model-okunur reason verisidir, çünkü
11
+ # model girdisinde "her başarısızlık veridir" (retry feedback'i
12
+ # message+hint'in birebir yankısıdır).
13
+ #
14
+ # Doküman ve value içerikleri daima string-key JSON şekli kalır
15
+ # (storage round-trip'inde drift olmaz); yalnızca op'un kendi kontrol
16
+ # alanları (:op, :path, :tokens, :index) semboldür.
17
+ class Patch
18
+ OP_VERBS = ["add", "replace", "remove"].freeze
19
+ OP_KEYS = ["op", "path", "value"].freeze
20
+ HEADER_KEYS = ["rev", "ops"].freeze
21
+
22
+ attr_reader :rev, :ops, :raw_bytesize
23
+
24
+ def initialize(rev, ops, raw_bytesize)
25
+ @rev = rev
26
+ @ops = ops
27
+ @raw_bytesize = raw_bytesize
28
+ freeze
29
+ end
30
+
31
+ # -> [:ok, Patch] | [:rejected, [reason, ...]]
32
+ def self.parse(text, max_bytes: MAX_PATCH_BYTES)
33
+ body = Rubik::Platform.safe_utf8(text.to_s)
34
+ if body.bytesize > max_bytes
35
+ return [:rejected, [GoalLedger.reason(
36
+ :syntax, op_index: nil, path: "",
37
+ message: "patch body is #{body.bytesize} bytes; the cap is #{max_bytes}",
38
+ hint: "send a smaller patch — split the work across several update calls"
39
+ )]]
40
+ end
41
+
42
+ begin
43
+ doc = JSON.parse(body)
44
+ rescue JSON::ParserError => e
45
+ return [:rejected, [GoalLedger.reason(
46
+ :syntax, op_index: nil, path: "",
47
+ message: "invalid JSON: #{e.message.to_s.slice(0, 120)}",
48
+ hint: "strict JSON only: no trailing commas, no comments, quote every key and string"
49
+ )]]
50
+ end
51
+
52
+ header_reasons = header_reasons(doc)
53
+ return [:rejected, header_reasons] unless header_reasons.empty?
54
+
55
+ rev = doc["rev"]
56
+ unless rev.is_a?(Integer) && rev >= 0
57
+ return [:rejected, [GoalLedger.reason(
58
+ :schema, op_index: nil, path: "",
59
+ message: "rev must be a non-negative integer (got #{GoalLedger.json_type_name(rev)})",
60
+ hint: "send the ledger's current rev in the patch header"
61
+ )]]
62
+ end
63
+
64
+ ops_in = doc["ops"]
65
+ if !ops_in.is_a?(Array) || ops_in.empty?
66
+ return [:rejected, [GoalLedger.reason(
67
+ :schema, op_index: nil, path: "",
68
+ message: "ops must be a non-empty JSON array (got #{GoalLedger.json_type_name(ops_in)})",
69
+ hint: "carry at least one op — an empty patch would bump rev without changing anything"
70
+ )]]
71
+ end
72
+ if ops_in.size > MAX_OPS
73
+ return [:rejected, [GoalLedger.reason(
74
+ :schema, op_index: nil, path: "",
75
+ message: "patch carries #{ops_in.size} ops; the cap is #{MAX_OPS}",
76
+ hint: "split the work across several update calls"
77
+ )]]
78
+ end
79
+
80
+ reasons = []
81
+ ops = []
82
+ ops_in.each_with_index do |raw, idx|
83
+ op, errs = normalize_op(raw, idx)
84
+ reasons.concat(errs)
85
+ ops << op if op
86
+ end
87
+ return [:rejected, reasons] unless reasons.empty?
88
+
89
+ [:ok, new(rev, ops.freeze, body.bytesize)]
90
+ end
91
+
92
+ # ---- sınıf-içi (private semantik; module_function köprüsü şart değil)
93
+
94
+ def self.header_reasons(doc)
95
+ unless doc.is_a?(Hash)
96
+ return [GoalLedger.reason(
97
+ :schema, op_index: nil, path: "",
98
+ message: "the patch must be a single JSON object (got #{GoalLedger.json_type_name(doc)})",
99
+ hint: 'the patch shape is {"rev": <current ledger rev>, "ops": [ ... ]}'
100
+ )]
101
+ end
102
+
103
+ reasons = []
104
+ missing = HEADER_KEYS - doc.keys
105
+ unknown = doc.keys - HEADER_KEYS
106
+ unless missing.empty?
107
+ reasons << GoalLedger.reason(
108
+ :schema, op_index: nil, path: "",
109
+ message: "patch is missing the #{missing.join(', ')} key(s)",
110
+ hint: 'the patch header is {"rev": <current ledger rev>, "ops": [ ... ]}'
111
+ )
112
+ end
113
+ unless unknown.empty?
114
+ reasons << GoalLedger.reason(
115
+ :schema, op_index: nil, path: "",
116
+ message: "unknown patch key(s) #{unknown.join(', ')}",
117
+ hint: "allowed top-level keys: rev, ops"
118
+ )
119
+ end
120
+ reasons
121
+ end
122
+
123
+ def self.normalize_op(raw, idx)
124
+ return [nil, [op_shape_reason(idx)]] unless raw.is_a?(Hash)
125
+
126
+ unknown = raw.keys - OP_KEYS
127
+ unless unknown.empty?
128
+ return [nil, [GoalLedger.reason(
129
+ :schema, op_index: idx, path: "",
130
+ message: "op #{idx} carries unknown key(s) #{unknown.join(', ')}",
131
+ hint: "allowed op keys: op, path, value"
132
+ )]]
133
+ end
134
+
135
+ verb = raw["op"]
136
+ unless OP_VERBS.include?(verb)
137
+ return [nil, [GoalLedger.reason(
138
+ :schema, op_index: idx, path: "",
139
+ message: "op #{idx} has unknown verb #{GoalLedger.json_type_name(verb)}#{verb_inspect(verb)}",
140
+ hint: "allowed verbs: add, replace, remove"
141
+ )]]
142
+ end
143
+
144
+ path = raw["path"]
145
+ unless path.is_a?(String)
146
+ return [nil, [GoalLedger.reason(
147
+ :schema, op_index: idx, path: "",
148
+ message: "op #{idx} path must be a JSON string (got #{GoalLedger.json_type_name(path)})",
149
+ hint: 'address sections with JSON pointers like "/tasks/3/status" or "/findings/-"'
150
+ )]]
151
+ end
152
+
153
+ unless path.start_with?("/")
154
+ return [nil, [GoalLedger.reason(
155
+ :schema, op_index: idx, path: path,
156
+ message: "op #{idx} path '#{path}' is not a JSON pointer",
157
+ hint: 'pointers start with "/" — use /section, /section/idx, /section/idx/field or /section/-'
158
+ )]]
159
+ end
160
+
161
+ if path == "/"
162
+ return [nil, [GoalLedger.reason(
163
+ :schema, op_index: idx, path: path,
164
+ message: "op #{idx} addresses the whole ledger root",
165
+ hint: "the ledger root is not replaceable; address sections individually " \
166
+ "(/tasks/3/status, /findings/-, ...)"
167
+ )]]
168
+ end
169
+
170
+ tokens, err = tokenize(path, idx)
171
+ return [nil, [err]] if err
172
+
173
+ if verb == "remove" && raw.key?("value")
174
+ return [nil, [GoalLedger.reason(
175
+ :schema, op_index: idx, path: path,
176
+ message: "op #{idx} (remove) must not carry a value",
177
+ hint: "to change a value use replace; remove deletes the item outright"
178
+ )]]
179
+ end
180
+ unless verb == "remove" || raw.key?("value")
181
+ return [nil, [GoalLedger.reason(
182
+ :schema, op_index: idx, path: path,
183
+ message: "op #{idx} (#{verb}) is missing its value",
184
+ hint: 'carry the value in the op: {"op": "add", "path": "/findings/-", "value": ...}'
185
+ )]]
186
+ end
187
+
188
+ if hyphen_violation(tokens, verb)
189
+ return [nil, [GoalLedger.reason(
190
+ :schema, op_index: idx, path: path,
191
+ message: "op #{idx} path '#{path}' uses '-' outside an append position",
192
+ hint: "'-' appends and may only be the final token of an add path " \
193
+ "(e.g. /findings/-); use a numeric index for existing items"
194
+ )]]
195
+ end
196
+
197
+ value = verb == "remove" ? nil : deep_freeze(raw["value"])
198
+ [{ op: verb.to_sym, path: path, tokens: tokens, value: value, index: idx }.freeze, []]
199
+ end
200
+
201
+ def self.op_shape_reason(idx)
202
+ GoalLedger.reason(
203
+ :schema, op_index: idx, path: "",
204
+ message: "op #{idx} must be a JSON object (got non-object)",
205
+ hint: 'each op looks like {"op": "add", "path": "/findings/-", "value": ...}'
206
+ )
207
+ end
208
+
209
+ def self.verb_inspect(verb)
210
+ verb.is_a?(String) ? " '#{verb}'" : ""
211
+ end
212
+
213
+ # JSON-pointer tokenize: ~0 → ~, ~1 → / (yalnızbu iki escape meşru);
214
+ # boş segment (//) geçersiz. Dönen dizi frozen'dır.
215
+ def self.tokenize(path, idx)
216
+ raw_tokens = path.split("/", -1)[1..]
217
+ tokens = []
218
+ raw_tokens.each do |seg|
219
+ if seg.empty?
220
+ return nil, GoalLedger.reason(
221
+ :schema, op_index: idx, path: path,
222
+ message: "op #{idx} path '#{path}' has an empty pointer segment",
223
+ hint: "JSON pointers do not contain empty segments — use /section, /section/idx or /section/-"
224
+ )
225
+ end
226
+ if seg.include?("~")
227
+ unless seg.match?(/\A(?:[^~]|~0|~1)*\z/)
228
+ return nil, GoalLedger.reason(
229
+ :schema, op_index: idx, path: path,
230
+ message: "op #{idx} path '#{path}' uses an invalid escape in '#{seg}'",
231
+ hint: "only ~0 (literal tilde) and ~1 (literal slash) escapes exist in JSON pointers"
232
+ )
233
+ end
234
+
235
+ seg = seg.gsub("~1", "/").gsub("~0", "~")
236
+ end
237
+ tokens << seg
238
+ end
239
+ [tokens.freeze, nil]
240
+ end
241
+
242
+ # '-' yalnızca add path'inin SON token'ı olabilir; replace/remove
243
+ # hiçbir konumda '-' kullanamaz.
244
+ def self.hyphen_violation(tokens, verb)
245
+ tokens.each_with_index do |tok, pos|
246
+ return true if tok == "-" && !(pos == tokens.length - 1 && verb == "add")
247
+ end
248
+ nil
249
+ end
250
+
251
+ def self.deep_freeze(value)
252
+ case value
253
+ when Hash then value.each do |k, v|
254
+ deep_freeze(k)
255
+ deep_freeze(v)
256
+ end.freeze
257
+ when Array then value.each { |v| deep_freeze(v) }.freeze
258
+ when String then value.dup.freeze
259
+ else value # nil / true / false / Integer / Float zaten immutable
260
+ end
261
+ end
262
+
263
+ private_class_method :header_reasons, :normalize_op, :op_shape_reason, :verb_inspect,
264
+ :tokenize, :hyphen_violation, :deep_freeze
265
+ end
266
+ end
267
+ end
268
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rubik
4
+ module Orchestrator
5
+ module GoalLedger
6
+ # Deklaratif şema VO'ları — hepsi ctor sonunda dondurulur (Struct#freeze
7
+ # shallow kaldığı için kontrol bize ait). MEKANİZMA bu dosyanın işidir;
8
+ # Schema.goal_skeleton yalnızca bir ÖRNEKTİR — üretim şemasını feature
9
+ # katmanı (goal-ledger planı A) sahiplenir.
10
+ #
11
+ # Alan tipleri JSON dünyasındadır (:string/:integer/:boolean);
12
+ # `optional: true` alanlar whole-item şekillerde eksik kalabilir,
13
+ # `allow_null: true` alanlara null ATANABİLİR (null asla silmez —
14
+ # silme yalnız remove op'udur), `managed: true` root alanlarına patch
15
+ # pointer'ı yazamaz (rev savunması).
16
+ class FieldSpec
17
+ attr_reader :type, :enum, :max_len, :optional, :allow_null, :managed
18
+
19
+ def initialize(type:, enum: nil, max_len: nil, optional: false, allow_null: false, managed: false)
20
+ @type = type
21
+ @enum = enum&.dup&.freeze
22
+ @max_len = max_len
23
+ @optional = optional
24
+ @allow_null = allow_null
25
+ @managed = managed
26
+ freeze
27
+ end
28
+ end
29
+
30
+ # Liste-section öğe şekli: fields (ad → FieldSpec) + id_field
31
+ # (nil olmayan değer => koleksiyon içinde uniqueluk kuralı).
32
+ class ItemSpec
33
+ attr_reader :fields, :id_field
34
+
35
+ def initialize(fields:, id_field: nil)
36
+ @fields = fields.to_a.to_h { |name, spec| [name.dup.freeze, spec] }.freeze
37
+ @id_field = id_field&.dup&.freeze
38
+ freeze
39
+ end
40
+ end
41
+
42
+ # Section şekli: ya item'lı liste (item:) ya da düz string listesi
43
+ # (scalar: + max_len:). İkisi birden olmaz.
44
+ class SectionSpec
45
+ attr_reader :item, :scalar, :max_len
46
+
47
+ def initialize(item: nil, scalar: nil, max_len: nil)
48
+ raise ArgumentError, "a section is either an item list or a scalar list" if item && scalar
49
+
50
+ @item = item
51
+ @scalar = scalar
52
+ @max_len = max_len
53
+ freeze
54
+ end
55
+ end
56
+
57
+ class Schema
58
+ attr_reader :root_scalars, :sections
59
+
60
+ def initialize(root_scalars:, sections:)
61
+ @root_scalars = root_scalars.to_a.to_h { |name, spec| [name.dup.freeze, spec] }.freeze
62
+ @sections = sections.to_a.to_h { |name, spec| [name.dup.freeze, spec] }.freeze
63
+ freeze
64
+ end
65
+
66
+ # ÖRNEK goal şeması (C'nin ship ettiği örnek): beş section + iki root
67
+ # skalar. `verify` alanları (tasks, environment_notes) bilinçli olarak
68
+ # optional+allow_null taşınır — re-verification planı (B) bu alanları
69
+ # doldurur; şema mekanizması onları şimdiden taşır.
70
+ def self.goal_skeleton
71
+ new(
72
+ root_scalars: {
73
+ "rev" => FieldSpec.new(type: :integer, managed: true),
74
+ "objective" => FieldSpec.new(type: :string, max_len: 400)
75
+ },
76
+ sections: {
77
+ "tasks" => SectionSpec.new(item: ItemSpec.new(
78
+ fields: {
79
+ "id" => FieldSpec.new(type: :string, max_len: 40),
80
+ "title" => FieldSpec.new(type: :string, max_len: 200),
81
+ # stale (plan B): harness re-verification failed damgası —
82
+ # yalnız harness yazar; temizlik yalnız model ΔΣ patch'i.
83
+ "status" => FieldSpec.new(type: :string, enum: ["todo", "doing", "done", "blocked", "stale"]),
84
+ "notes" => FieldSpec.new(type: :string, max_len: 500, optional: true, allow_null: true),
85
+ # verify (plan B): VerifySpec gramerine göre obje — validator
86
+ # yalnız SHAPE'ı (object/null) zorlar; içerik doğrulaması
87
+ # koşu zamanında tri-state unknown yoludur.
88
+ "verify" => FieldSpec.new(type: :object, optional: true, allow_null: true)
89
+ },
90
+ id_field: "id"
91
+ )),
92
+ "findings" => SectionSpec.new(item: ItemSpec.new(
93
+ fields: {
94
+ "id" => FieldSpec.new(type: :string, max_len: 40),
95
+ "text" => FieldSpec.new(type: :string, max_len: 800),
96
+ "source_step" => FieldSpec.new(type: :integer)
97
+ },
98
+ id_field: "id"
99
+ )),
100
+ "decisions" => SectionSpec.new(item: ItemSpec.new(
101
+ fields: {
102
+ "id" => FieldSpec.new(type: :string, max_len: 40),
103
+ "text" => FieldSpec.new(type: :string, max_len: 800),
104
+ "rationale" => FieldSpec.new(type: :string, max_len: 500)
105
+ },
106
+ id_field: "id"
107
+ )),
108
+ "open_questions" => SectionSpec.new(scalar: :string, max_len: 400),
109
+ "environment_notes" => SectionSpec.new(item: ItemSpec.new(
110
+ fields: {
111
+ "id" => FieldSpec.new(type: :string, max_len: 40),
112
+ "text" => FieldSpec.new(type: :string, max_len: 800),
113
+ "verify" => FieldSpec.new(type: :object, optional: true, allow_null: true)
114
+ },
115
+ id_field: "id"
116
+ ))
117
+ }
118
+ )
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end