rosett-ai 1.3.3 → 1.5.10

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 (453) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +413 -22
  3. data/README.md +56 -42
  4. data/bin/raictl +13 -0
  5. data/conf/adopt_redactions.yml +2 -0
  6. data/conf/compliance/cra_rules.yml +2 -0
  7. data/conf/compliance/license_rules.yml +2 -0
  8. data/conf/mcp/server_defaults.yml +2 -0
  9. data/conf/mcp/trust.yml +1 -0
  10. data/conf/packaging/core.yml +2 -0
  11. data/conf/packaging/gtk4.yml +2 -0
  12. data/conf/packaging/qt6.yml +2 -0
  13. data/conf/policy/default_deny_list.yml +2 -0
  14. data/conf/review/cli-command-audit.yml +2 -0
  15. data/conf/review/design-docs.yml +2 -0
  16. data/conf/review/design-questionnaire.yml +2 -0
  17. data/conf/review/questionnaire.yml +2 -0
  18. data/conf/review/rosett-ai-core.yml +2 -0
  19. data/conf/schemas/ai_config_schema.json +14 -2
  20. data/conf/schemas/autoqueue.schema.json +153 -0
  21. data/conf/schemas/behaviour_schema.json +31 -7
  22. data/conf/schemas/compliance_rule_schema.json +17 -4
  23. data/conf/schemas/content_pack_manifest_schema.json +23 -4
  24. data/conf/schemas/design_schema.json +177 -42
  25. data/conf/schemas/engine_manifest_schema.json +22 -5
  26. data/conf/schemas/lockfile_schema.json +26 -4
  27. data/conf/schemas/mcp_server_schema.json +50 -9
  28. data/conf/schemas/packaging_schema.json +23 -5
  29. data/conf/schemas/policy_schema.json +35 -9
  30. data/conf/schemas/provenance_schema.json +33 -5
  31. data/conf/schemas/rai_config_schema.json +6 -2
  32. data/conf/schemas/rai_project_schema.json +8 -2
  33. data/conf/schemas/scope_hierarchy_schema.json +22 -5
  34. data/conf/schemas/target_schema.json +33 -6
  35. data/conf/schemas/tooling_schema.json +53 -12
  36. data/conf/schemas/workflow_schema.json +39 -9
  37. data/conf/targets/agents_md.yml +1 -0
  38. data/conf/targets/claude.yml +2 -0
  39. data/conf/tooling/tools.yml +2 -0
  40. data/lib/rosett_ai/adopter/local_analysis_collector.rb +5 -0
  41. data/lib/rosett_ai/adopter/rule_adopter.rb +40 -0
  42. data/lib/rosett_ai/ai_config/context_window.rb +3 -0
  43. data/lib/rosett_ai/ai_config/cost_controls.rb +1 -0
  44. data/lib/rosett_ai/ai_config/fallback_chain.rb +1 -0
  45. data/lib/rosett_ai/ai_config/model_router.rb +1 -0
  46. data/lib/rosett_ai/ai_config/validator.rb +2 -0
  47. data/lib/rosett_ai/authorship/attribution_compiler.rb +1 -0
  48. data/lib/rosett_ai/authorship/disclosure_policy.rb +2 -0
  49. data/lib/rosett_ai/authorship/review_validator.rb +1 -0
  50. data/lib/rosett_ai/authorship/trailer_generator.rb +1 -0
  51. data/lib/rosett_ai/backup/compressor.rb +3 -0
  52. data/lib/rosett_ai/backup/destination.rb +1 -0
  53. data/lib/rosett_ai/behaviour/manager.rb +1 -0
  54. data/lib/rosett_ai/compiler/backend.rb +3 -0
  55. data/lib/rosett_ai/compiler/backends/agents_md_backend.rb +1 -0
  56. data/lib/rosett_ai/compiler/backends/claude_backend.rb +1 -0
  57. data/lib/rosett_ai/compiler/behaviour_compiler.rb +1 -0
  58. data/lib/rosett_ai/compiler/compilation_pipeline.rb +1 -7
  59. data/lib/rosett_ai/compiler/locale_compiler.rb +2 -0
  60. data/lib/rosett_ai/compiler/target_profile.rb +11 -0
  61. data/lib/rosett_ai/completion/generator.rb +2 -0
  62. data/lib/rosett_ai/completion/shells/zsh_generator.rb +1 -0
  63. data/lib/rosett_ai/comply/checkers/cra_checker.rb +1 -0
  64. data/lib/rosett_ai/comply/checkers/license_checker.rb +2 -0
  65. data/lib/rosett_ai/comply/checkers/spdx_header_checker.rb +4 -0
  66. data/lib/rosett_ai/comply/runner.rb +2 -0
  67. data/lib/rosett_ai/composition/composer.rb +1 -0
  68. data/lib/rosett_ai/composition/lockfile.rb +1 -0
  69. data/lib/rosett_ai/composition/merge_strategy.rb +1 -0
  70. data/lib/rosett_ai/config/masking_secret_resolver.rb +9 -0
  71. data/lib/rosett_ai/config/secret_resolver.rb +4 -0
  72. data/lib/rosett_ai/configuration.rb +4 -0
  73. data/lib/rosett_ai/content/content_client.rb +15 -0
  74. data/lib/rosett_ai/content/pack_installer.rb +1 -0
  75. data/lib/rosett_ai/content/pack_manifest.rb +21 -0
  76. data/lib/rosett_ai/content_packs/manager.rb +1 -0
  77. data/lib/rosett_ai/dbus/compositor_detector.rb +2 -0
  78. data/lib/rosett_ai/dbus/focus_adapters/base.rb +1 -0
  79. data/lib/rosett_ai/dbus/focus_adapters/gnome_adapter.rb +5 -0
  80. data/lib/rosett_ai/dbus/focus_adapters/hyprland_adapter.rb +4 -0
  81. data/lib/rosett_ai/dbus/focus_adapters/i3_adapter.rb +4 -0
  82. data/lib/rosett_ai/dbus/focus_adapters/kwin_adapter.rb +4 -0
  83. data/lib/rosett_ai/dbus/focus_adapters/x11_adapter.rb +4 -0
  84. data/lib/rosett_ai/dbus/focus_monitor_interface.rb +1 -0
  85. data/lib/rosett_ai/dbus/manager_interface.rb +1 -0
  86. data/lib/rosett_ai/dbus/plugin_manager_interface.rb +2 -0
  87. data/lib/rosett_ai/dbus/rate_limiter.rb +1 -0
  88. data/lib/rosett_ai/dbus/service.rb +3 -0
  89. data/lib/rosett_ai/dbus/status_notifier_interface.rb +14 -0
  90. data/lib/rosett_ai/desktop/dbus_client.rb +6 -0
  91. data/lib/rosett_ai/desktop/gtk4_app.rb +3 -0
  92. data/lib/rosett_ai/desktop/gtk4_preferences.rb +1 -0
  93. data/lib/rosett_ai/desktop/gui_logger.rb +2 -0
  94. data/lib/rosett_ai/doctor/check.rb +1 -1
  95. data/lib/rosett_ai/doctor/checks/cache_health_check.rb +1 -0
  96. data/lib/rosett_ai/doctor.rb +1 -0
  97. data/lib/rosett_ai/documentation/reference_compiler.rb +9 -0
  98. data/lib/rosett_ai/documentation/translator.rb +8 -0
  99. data/lib/rosett_ai/engines/base_config_compiler.rb +71 -1
  100. data/lib/rosett_ai/engines/detector.rb +5 -0
  101. data/lib/rosett_ai/engines/registry.rb +7 -0
  102. data/lib/rosett_ai/exit_codes.rb +6 -0
  103. data/lib/rosett_ai/formatting.rb +1 -0
  104. data/lib/rosett_ai/gem_consistency_checker.rb +6 -0
  105. data/lib/rosett_ai/git_hooks/chain_detector.rb +1 -0
  106. data/lib/rosett_ai/git_hooks/installer.rb +4 -0
  107. data/lib/rosett_ai/git_hooks/script_generator.rb +2 -0
  108. data/lib/rosett_ai/gitlab/validators/supplementary_gitlab_ci_yaml_validator.rb +6 -0
  109. data/lib/rosett_ai/i18n/utf8_checker.rb +1 -0
  110. data/lib/rosett_ai/init/config_file_writer.rb +4 -0
  111. data/lib/rosett_ai/init/directory_builder.rb +15 -0
  112. data/lib/rosett_ai/init/file_copier.rb +51 -0
  113. data/lib/rosett_ai/init/mcp_registrar.rb +1 -0
  114. data/lib/rosett_ai/init/project_initializer.rb +1 -0
  115. data/lib/rosett_ai/licensing/license_key.rb +4 -0
  116. data/lib/rosett_ai/licensing/license_store.rb +11 -0
  117. data/lib/rosett_ai/licensing/tier.rb +9 -0
  118. data/lib/rosett_ai/mcp/admin/auditor.rb +1 -0
  119. data/lib/rosett_ai/mcp/admin/health_checker.rb +1 -0
  120. data/lib/rosett_ai/mcp/admin/registry.rb +1 -0
  121. data/lib/rosett_ai/mcp/enforcement/hook_generator.rb +27 -4
  122. data/lib/rosett_ai/mcp/enforcement/validator.rb +24 -3
  123. data/lib/rosett_ai/mcp/governance.rb +9 -0
  124. data/lib/rosett_ai/mcp/http_security_config.rb +22 -0
  125. data/lib/rosett_ai/mcp/key_hasher.rb +4 -2
  126. data/lib/rosett_ai/mcp/keyfile.rb +4 -0
  127. data/lib/rosett_ai/mcp/middleware/cors.rb +4 -0
  128. data/lib/rosett_ai/mcp/middleware/origin_validation.rb +1 -0
  129. data/lib/rosett_ai/mcp/middleware/rate_limit.rb +3 -0
  130. data/lib/rosett_ai/mcp/plugins.rb +1 -0
  131. data/lib/rosett_ai/mcp/prompts/compilation_prompt.rb +2 -0
  132. data/lib/rosett_ai/mcp/prompts/compliance_prompt.rb +3 -0
  133. data/lib/rosett_ai/mcp/prompts/diagnostics_prompt.rb +2 -0
  134. data/lib/rosett_ai/mcp/prompts/validation_prompt.rb +2 -0
  135. data/lib/rosett_ai/mcp/resources/behaviour_resource.rb +2 -0
  136. data/lib/rosett_ai/mcp/resources/config_resource.rb +2 -0
  137. data/lib/rosett_ai/mcp/resources/design_resource.rb +1 -0
  138. data/lib/rosett_ai/mcp/resources/hooks_resource.rb +3 -0
  139. data/lib/rosett_ai/mcp/resources/provenance_resource.rb +2 -0
  140. data/lib/rosett_ai/mcp/resources/rules_resource.rb +1 -0
  141. data/lib/rosett_ai/mcp/resources/schema_resource.rb +1 -0
  142. data/lib/rosett_ai/mcp/server.rb +7 -1
  143. data/lib/rosett_ai/mcp/settings/trust_manager.rb +3 -0
  144. data/lib/rosett_ai/mcp/tools/adopt_tool.rb +4 -0
  145. data/lib/rosett_ai/mcp/tools/backup_tool.rb +4 -0
  146. data/lib/rosett_ai/mcp/tools/behaviour_display_tool.rb +4 -0
  147. data/lib/rosett_ai/mcp/tools/behaviour_list_tool.rb +3 -0
  148. data/lib/rosett_ai/mcp/tools/behaviour_manage_tool.rb +5 -0
  149. data/lib/rosett_ai/mcp/tools/behaviour_show_tool.rb +4 -0
  150. data/lib/rosett_ai/mcp/tools/compile_status_tool.rb +4 -0
  151. data/lib/rosett_ai/mcp/tools/compile_tool.rb +4 -0
  152. data/lib/rosett_ai/mcp/tools/comply_tool.rb +4 -0
  153. data/lib/rosett_ai/mcp/tools/config_compile_tool.rb +4 -0
  154. data/lib/rosett_ai/mcp/tools/config_status_tool.rb +3 -0
  155. data/lib/rosett_ai/mcp/tools/content_tool.rb +5 -0
  156. data/lib/rosett_ai/mcp/tools/context_query_tool.rb +4 -0
  157. data/lib/rosett_ai/mcp/tools/design_list_tool.rb +3 -0
  158. data/lib/rosett_ai/mcp/tools/design_show_tool.rb +4 -0
  159. data/lib/rosett_ai/mcp/tools/doctor_tool.rb +4 -0
  160. data/lib/rosett_ai/mcp/tools/documentation_status_tool.rb +3 -0
  161. data/lib/rosett_ai/mcp/tools/engines_tool.rb +5 -0
  162. data/lib/rosett_ai/mcp/tools/hook_install_tool.rb +7 -0
  163. data/lib/rosett_ai/mcp/tools/hook_preview_tool.rb +33 -8
  164. data/lib/rosett_ai/mcp/tools/hooks_status_tool.rb +5 -0
  165. data/lib/rosett_ai/mcp/tools/init_tool.rb +5 -0
  166. data/lib/rosett_ai/mcp/tools/license_status_tool.rb +3 -0
  167. data/lib/rosett_ai/mcp/tools/project_tool.rb +5 -0
  168. data/lib/rosett_ai/mcp/tools/provenance_tool.rb +5 -0
  169. data/lib/rosett_ai/mcp/tools/provenance_write_tool.rb +3 -0
  170. data/lib/rosett_ai/mcp/tools/retrofit_tool.rb +5 -0
  171. data/lib/rosett_ai/mcp/tools/rule_search_tool.rb +4 -0
  172. data/lib/rosett_ai/mcp/tools/schema_get_tool.rb +5 -0
  173. data/lib/rosett_ai/mcp/tools/tooling_tool.rb +5 -0
  174. data/lib/rosett_ai/mcp/tools/validate_tool.rb +4 -0
  175. data/lib/rosett_ai/mcp/tools/workflow_execute_tool.rb +4 -0
  176. data/lib/rosett_ai/mcp/tools/workflow_tool.rb +5 -0
  177. data/lib/rosett_ai/migration/detector.rb +1 -0
  178. data/lib/rosett_ai/migration/nncc_config_migrator.rb +2 -0
  179. data/lib/rosett_ai/migration/xdg_migrator.rb +3 -1
  180. data/lib/rosett_ai/package_manager/apt.rb +5 -0
  181. data/lib/rosett_ai/package_manager/base.rb +1 -0
  182. data/lib/rosett_ai/package_manager/gem_backend.rb +1 -0
  183. data/lib/rosett_ai/packaging/variant_config.rb +12 -0
  184. data/lib/rosett_ai/plugins/contract.rb +1 -0
  185. data/lib/rosett_ai/plugins/engine_contract.rb +2 -0
  186. data/lib/rosett_ai/plugins/gui_contract.rb +2 -0
  187. data/lib/rosett_ai/plugins/mcp_contract.rb +2 -0
  188. data/lib/rosett_ai/plugins/registry.rb +10 -0
  189. data/lib/rosett_ai/policy/auditor.rb +1 -0
  190. data/lib/rosett_ai/policy/opt_out_scanner.rb +1 -0
  191. data/lib/rosett_ai/policy/tier_hierarchy.rb +2 -0
  192. data/lib/rosett_ai/policy/validator.rb +1 -0
  193. data/lib/rosett_ai/profiler.rb +7 -0
  194. data/lib/rosett_ai/project/drift_detector.rb +1 -0
  195. data/lib/rosett_ai/project/manager.rb +1 -0
  196. data/lib/rosett_ai/project/template_applier.rb +1 -0
  197. data/lib/rosett_ai/provenance/entry.rb +2 -0
  198. data/lib/rosett_ai/provenance/source.rb +1 -0
  199. data/lib/rosett_ai/provenance/store.rb +3 -0
  200. data/lib/rosett_ai/provenance/validator.rb +1 -0
  201. data/lib/rosett_ai/quorum/collector.rb +1 -0
  202. data/lib/rosett_ai/quorum/comparator.rb +2 -0
  203. data/lib/rosett_ai/quorum/strategies/adopt.rb +1 -0
  204. data/lib/rosett_ai/rai_config.rb +9 -0
  205. data/lib/rosett_ai/retrofit/engine.rb +2 -0
  206. data/lib/rosett_ai/retrofit/parsers/agents_md_parser.rb +8 -0
  207. data/lib/rosett_ai/retrofit/parsers/claude_parser.rb +10 -0
  208. data/lib/rosett_ai/retrofit/parsers/cursor_parser.rb +9 -0
  209. data/lib/rosett_ai/retrofit/secret_detector.rb +3 -0
  210. data/lib/rosett_ai/secrets_resolver.rb +1 -0
  211. data/lib/rosett_ai/session_hooks/installer.rb +130 -0
  212. data/lib/rosett_ai/smart_feedback/suggester.rb +4 -0
  213. data/lib/rosett_ai/smart_feedback/thor_middleware.rb +7 -0
  214. data/lib/rosett_ai/structured_logger.rb +1 -0
  215. data/lib/rosett_ai/telemetry/log_rotator.rb +1 -0
  216. data/lib/rosett_ai/telemetry.rb +1 -0
  217. data/lib/rosett_ai/thor/cli.rb +9 -0
  218. data/lib/rosett_ai/thor/tasks/adopt.rb +2 -0
  219. data/lib/rosett_ai/thor/tasks/backfill_user_yml.rb +207 -0
  220. data/lib/rosett_ai/thor/tasks/backup.rb +6 -0
  221. data/lib/rosett_ai/thor/tasks/behaviour.rb +12 -0
  222. data/lib/rosett_ai/thor/tasks/build.rb +106 -21
  223. data/lib/rosett_ai/thor/tasks/compile.rb +76 -32
  224. data/lib/rosett_ai/thor/tasks/completion.rb +9 -0
  225. data/lib/rosett_ai/thor/tasks/comply.rb +2 -0
  226. data/lib/rosett_ai/thor/tasks/config.rb +21 -5
  227. data/lib/rosett_ai/thor/tasks/content.rb +8 -0
  228. data/lib/rosett_ai/thor/tasks/dbus.rb +13 -0
  229. data/lib/rosett_ai/thor/tasks/design.rb +2 -0
  230. data/lib/rosett_ai/thor/tasks/desktop.rb +4 -0
  231. data/lib/rosett_ai/thor/tasks/doctor.rb +2 -0
  232. data/lib/rosett_ai/thor/tasks/documentation.rb +7 -0
  233. data/lib/rosett_ai/thor/tasks/engines.rb +8 -0
  234. data/lib/rosett_ai/thor/tasks/hooks.rb +8 -0
  235. data/lib/rosett_ai/thor/tasks/init.rb +1 -0
  236. data/lib/rosett_ai/thor/tasks/license.rb +8 -0
  237. data/lib/rosett_ai/thor/tasks/mcp.rb +40 -0
  238. data/lib/rosett_ai/thor/tasks/migrate.rb +2 -0
  239. data/lib/rosett_ai/thor/tasks/plugins.rb +15 -0
  240. data/lib/rosett_ai/thor/tasks/project.rb +8 -0
  241. data/lib/rosett_ai/thor/tasks/provenance.rb +8 -0
  242. data/lib/rosett_ai/thor/tasks/release.rb +8 -0
  243. data/lib/rosett_ai/thor/tasks/retrofit.rb +4 -0
  244. data/lib/rosett_ai/thor/tasks/validate.rb +1 -0
  245. data/lib/rosett_ai/thor/tasks/workflow.rb +14 -0
  246. data/lib/rosett_ai/tooling/version_checker.rb +1 -0
  247. data/lib/rosett_ai/ui/accessible_tui.rb +18 -0
  248. data/lib/rosett_ai/ui/base.rb +3 -0
  249. data/lib/rosett_ai/ui/gtk4.rb +19 -0
  250. data/lib/rosett_ai/ui/kde.rb +1 -0
  251. data/lib/rosett_ai/ui/qt6.rb +1 -0
  252. data/lib/rosett_ai/ui/registry.rb +15 -0
  253. data/lib/rosett_ai/ui/tui.rb +22 -0
  254. data/lib/rosett_ai/validators/behaviour_validator.rb +1 -0
  255. data/lib/rosett_ai/validators/design_validator.rb +2 -0
  256. data/lib/rosett_ai/validators/schema_validator.rb +4 -0
  257. data/lib/rosett_ai/validators/tooling_validator.rb +1 -0
  258. data/lib/rosett_ai/version.rb +2 -1
  259. data/lib/rosett_ai/version_consistency_checker.rb +6 -0
  260. data/lib/rosett_ai/workflow/engine.rb +1 -0
  261. data/lib/rosett_ai/workflow/manager.rb +1 -0
  262. data/lib/rosett_ai/workflow/schema_validator.rb +2 -0
  263. data/lib/rosett_ai/workflow/step_runner.rb +1 -0
  264. data/lib/rosett_ai/workflow/steps/rai_step.rb +1 -0
  265. data/lib/rosett_ai/yaml_loader.rb +2 -0
  266. data/lib/rosett_ai.rb +2 -0
  267. data/lib/scripts/generated/docker_hub_tags.rb +2 -0
  268. data/locales/ar.yml +2 -0
  269. data/locales/en.yml +5 -0
  270. data/locales/fr.yml +2 -0
  271. metadata +9 -189
  272. data/.ai-provenance.yml +0 -119
  273. data/.debride_whitelist +0 -186
  274. data/.fasterer.yml +0 -29
  275. data/.mdl_style.rb +0 -10
  276. data/.mdlrc +0 -3
  277. data/.mutant.yml +0 -49
  278. data/.namespace-allowlist +0 -42
  279. data/.reek.yml +0 -1040
  280. data/.rosett-ai/config.yml +0 -3
  281. data/.rspec +0 -5
  282. data/.rubocop.yml +0 -380
  283. data/.ruby-version +0 -1
  284. data/.yamllint +0 -51
  285. data/.yardopts +0 -12
  286. data/AI-DISCLOSURE.md +0 -48
  287. data/CLAUDE.md +0 -141
  288. data/CONTRIBUTING.md +0 -734
  289. data/INSTALL.md +0 -154
  290. data/LICENSE.md +0 -675
  291. data/QUICKSTART.md +0 -73
  292. data/Rakefile +0 -200
  293. data/SECURITY.md +0 -114
  294. data/cliff.toml +0 -52
  295. data/conf/behaviour/.gitkeep +0 -0
  296. data/conf/design/aaif_alignment.yml +0 -181
  297. data/conf/design/ab_testing.yml +0 -172
  298. data/conf/design/accessibility.yml +0 -84
  299. data/conf/design/ai_authorship.yml +0 -210
  300. data/conf/design/ai_provenance.yml +0 -224
  301. data/conf/design/ai_tool_configuration.yml +0 -207
  302. data/conf/design/architecture.yml +0 -139
  303. data/conf/design/autocompletion.yml +0 -115
  304. data/conf/design/backward_compatibility.yml +0 -112
  305. data/conf/design/behaviour_composition.yml +0 -246
  306. data/conf/design/build_rake_extraction.yml +0 -57
  307. data/conf/design/ci_pipeline.yml +0 -100
  308. data/conf/design/claude_code_configuration.yml +0 -157
  309. data/conf/design/compiler.yml +0 -128
  310. data/conf/design/comply.yml +0 -153
  311. data/conf/design/content_packs.yml +0 -84
  312. data/conf/design/desktop_integration.yml +0 -289
  313. data/conf/design/distribution.yml +0 -216
  314. data/conf/design/doctor.yml +0 -184
  315. data/conf/design/documentation.yml +0 -152
  316. data/conf/design/engine_architecture.yml +0 -257
  317. data/conf/design/error_handling.yml +0 -103
  318. data/conf/design/feature_flags.yml +0 -142
  319. data/conf/design/git_hooks.yml +0 -165
  320. data/conf/design/gui_plugins.yml +0 -475
  321. data/conf/design/i18n.yml +0 -84
  322. data/conf/design/integration_testing.yml +0 -56
  323. data/conf/design/licensing_system.yml +0 -88
  324. data/conf/design/lifecycle_management.yml +0 -208
  325. data/conf/design/mcp_integration.yml +0 -207
  326. data/conf/design/mcp_settings.yml +0 -126
  327. data/conf/design/migration.yml +0 -56
  328. data/conf/design/monitoring_observability.yml +0 -194
  329. data/conf/design/namespace_cleanup.yml +0 -145
  330. data/conf/design/plugin_test_segregation.yml +0 -145
  331. data/conf/design/policy_management.yml +0 -229
  332. data/conf/design/project_management.yml +0 -183
  333. data/conf/design/rai_mcp_asset_discovery.yml +0 -164
  334. data/conf/design/rai_mcp_server.yml +0 -605
  335. data/conf/design/release_management.yml +0 -117
  336. data/conf/design/retrofit.yml +0 -199
  337. data/conf/design/retrospective_analyzer.yml +0 -79
  338. data/conf/design/scope_hierarchy.yml +0 -352
  339. data/conf/design/security.yml +0 -115
  340. data/conf/design/session_retrospective.yml +0 -85
  341. data/conf/design/smart_ui_feedback.yml +0 -89
  342. data/conf/design/structured_logging.yml +0 -148
  343. data/conf/design/styles.yml +0 -123
  344. data/conf/design/test_peer_review.yml +0 -89
  345. data/conf/design/testing.yml +0 -136
  346. data/conf/design/threat_model.yml +0 -108
  347. data/conf/design/ui_framework.yml +0 -111
  348. data/conf/design/usage_optimization.yml +0 -122
  349. data/conf/design/version_management.yml +0 -60
  350. data/conf/design/workflow.yml +0 -227
  351. data/doc/AAIF_POSITIONING.md +0 -58
  352. data/doc/ADOPT.md +0 -224
  353. data/doc/AI_PROVENANCE.md +0 -139
  354. data/doc/ARCHITECTURE.md +0 -920
  355. data/doc/BEHAVIOUR.md +0 -409
  356. data/doc/BUILD.md +0 -138
  357. data/doc/CI_CD_RECIPES.md +0 -171
  358. data/doc/CLAUDE_SESSIONS_MOVED.md +0 -16
  359. data/doc/COMMAND_ANALYSIS.md +0 -229
  360. data/doc/CONFIGURATION.md +0 -281
  361. data/doc/DESIGN_AUDIT.md +0 -235
  362. data/doc/DESIGN_PEER_REVIEW.md +0 -771
  363. data/doc/DESKTOP.md +0 -447
  364. data/doc/ENGINES.md +0 -567
  365. data/doc/ENGINE_DEVELOPMENT_GUIDE.md +0 -417
  366. data/doc/FEATURE_AUDIT.md +0 -218
  367. data/doc/IMPLEMENTATION_PLAN.md +0 -669
  368. data/doc/INCIDENT_REPORT_2026-02-02.md +0 -251
  369. data/doc/MIGRATION_GUIDE.md +0 -88
  370. data/doc/PACKAGING.md +0 -232
  371. data/doc/PROJECT_DASHBOARD.md +0 -153
  372. data/doc/PULP_DEPLOYMENT.md +0 -164
  373. data/doc/QUALITY_FIX_SUMMARY.md +0 -110
  374. data/doc/QUICK_START.md +0 -162
  375. data/doc/REEK_CONFIGURATION.md +0 -166
  376. data/doc/REFERENCE.md +0 -253
  377. data/doc/REFERENCES.md +0 -324
  378. data/doc/SECURITY_REVIEW_CHECKLIST.md +0 -72
  379. data/doc/SESSION_2026-02-28_GTK4_HARDENING.md +0 -359
  380. data/doc/SETUP.md +0 -202
  381. data/doc/TEST_PEER_REVIEW.md +0 -152
  382. data/doc/THREAT_MODEL.md +0 -230
  383. data/doc/USAGE.md +0 -545
  384. data/doc/USER_MANUAL.md +0 -585
  385. data/doc/ai_test_review_checklist.md +0 -110
  386. data/doc/changes/2026-02-18-packaging-fpm.md +0 -155
  387. data/doc/changes/2026-02-19-testing-infrastructure.md +0 -221
  388. data/doc/changes/2026-02-20-security-implementation.md +0 -281
  389. data/doc/changes/2026-02-20-styles-implementation.md +0 -220
  390. data/doc/changes/2026-02-21-architecture-completion.md +0 -95
  391. data/doc/changes/2026-02-21-architecture-ui-layer.md +0 -253
  392. data/doc/changes/2026-02-21-cc-config-implementation.md +0 -108
  393. data/doc/changes/2026-02-21-ci-pipeline-implementation.md +0 -214
  394. data/doc/changes/2026-02-21-compiler-multi-target-pipeline.md +0 -241
  395. data/doc/changes/2026-02-21-config-design-show-commands.md +0 -61
  396. data/doc/changes/2026-02-21-design-implementation-overview.md +0 -455
  397. data/doc/changes/2026-02-21-lifecycle-management.md +0 -196
  398. data/doc/changes/2026-02-21-path-resolver.md +0 -128
  399. data/doc/changes/2026-02-24-ci-tmpdir-mutant-fetch.md +0 -45
  400. data/doc/changes/2026-03-01-ci-bundler-strategy.md +0 -120
  401. data/doc/changes/2026-03-20-security-hardening-phase2.md +0 -163
  402. data/doc/context/SESSION-HANDOFF.md +0 -69
  403. data/doc/context/ai-engine-usage-trends-2026.md +0 -80
  404. data/doc/context/plan-pluggable-engines.md +0 -590
  405. data/doc/decisions/001-flog-deferred.md +0 -32
  406. data/doc/decisions/002-path-resolution-strategy.md +0 -158
  407. data/doc/decisions/003-ui-adapter-selection.md +0 -193
  408. data/doc/decisions/004-design-document-validation.md +0 -179
  409. data/doc/decisions/005-package-splitting-strategy.md +0 -200
  410. data/doc/decisions/006-multi-engine-architecture.md +0 -147
  411. data/doc/decisions/007-engine-agnostic-pivot.md +0 -219
  412. data/doc/decisions/008-ci-bundler-strategy.md +0 -129
  413. data/doc/decisions/009-core-only-v1-release.md +0 -60
  414. data/doc/decisions/010-engine-debian-packaging.md +0 -66
  415. data/doc/decisions/011-context-aware-cli.md +0 -71
  416. data/doc/dependency_decisions.yml +0 -247
  417. data/doc/issues/001-wrapper-missing-environment-variables.md +0 -197
  418. data/doc/issues/002-embedded-ruby-wrong-prefix.md +0 -217
  419. data/doc/issues/003-smoke-test-false-positive.md +0 -127
  420. data/doc/issues/004-market-research-design-updates.md +0 -109
  421. data/doc/issues/005-compile-scope-coexistence.md +0 -161
  422. data/doc/locales/.gitkeep +0 -0
  423. data/doc/man/rai.1.ronn +0 -505
  424. data/doc/operations/packaging.md +0 -133
  425. data/doc/operations/rosett-ai-release.md +0 -65
  426. data/doc/reference/error-catalog.md +0 -107
  427. data/doc/reference/rosett-ai-technical-reference.pdf +0 -0
  428. data/doc/reference/src/Pictures/cover.jpg +0 -0
  429. data/doc/reference/src/Pictures/head1.jpg +0 -0
  430. data/doc/reference/src/Pictures/head2.jpg +0 -0
  431. data/doc/reference/src/Pictures/head3.jpg +0 -0
  432. data/doc/reference/src/Pictures/head4.jpg +0 -0
  433. data/doc/reference/src/Pictures/head5.jpg +0 -0
  434. data/doc/reference/src/Pictures/head6.jpg +0 -0
  435. data/doc/reference/src/Pictures/head7.jpg +0 -0
  436. data/doc/reference/src/Pictures/head8.jpg +0 -0
  437. data/doc/reference/src/StyleInd.ist +0 -4
  438. data/doc/reference/src/bibliography.bib +0 -79
  439. data/doc/reference/src/main.tex +0 -1288
  440. data/doc/reference/src/structure.tex +0 -303
  441. data/doc/rosett-ai-bookmarks.html +0 -301
  442. data/kitchen.yml +0 -46
  443. data/lib/rosett_ai/mcp/enforcement/.gitkeep +0 -0
  444. data/locales/.gitkeep +0 -0
  445. data/packaging/build-engine-deb.sh +0 -81
  446. data/packaging/scripts/postinst +0 -17
  447. data/packaging/scripts/postrm +0 -19
  448. data/packaging/scripts/prerm +0 -10
  449. data/packaging/wrapper.sh.template +0 -38
  450. data/rosett-ai.gemspec +0 -63
  451. data/rules/.gitkeep +0 -0
  452. data/scripts/publish/pulp_upload.sh +0 -123
  453. data/settings.json +0 -29
@@ -22,6 +22,10 @@ module RosettAi
22
22
  :stage_timings, :completed_stages,
23
23
  :failed_stage, :failure_reason, :summary_printed
24
24
  ) do
25
+ # Create a new build context with a unique ID and log file.
26
+ #
27
+ # @param log_dir [Pathname] directory for build log files
28
+ # @return [BuildContext]
25
29
  def self.create(log_dir)
26
30
  id = SecureRandom.uuid
27
31
  log_path = log_dir.join("build-#{id}.log")
@@ -60,6 +64,7 @@ module RosettAi
60
64
 
61
65
  default_task :create
62
66
 
67
+ # @return [String] Target installation directory for packages.
63
68
  INSTALL_DIR = '/opt/rosett-ai'
64
69
 
65
70
  # Files/directories excluded when syncing app source into the staging tree
@@ -68,9 +73,12 @@ module RosettAi
68
73
  '.git', '.gitignore',
69
74
  # Bundler / gem build artifacts
70
75
  '.bundle', '.binstubs', 'vendor/bundle', '*.gem',
71
- 'Gemfile.dev', 'Gemfile.dev.lock', 'Gemfile.integration',
76
+ 'Gemfile.dev', 'Gemfile.dev.lock',
77
+ 'Gemfile.integration', 'Gemfile.integration.lock',
72
78
  # CI/CD
73
79
  '.gitlab-ci*',
80
+ # CI artifacts — never ship test/lint output
81
+ 'rspec-report.xml', 'rubocop-report.json',
74
82
  # Build & packaging
75
83
  'coverage', 'tmp', 'pkg', 'spec', 'test', 'packaging',
76
84
  # Dev tool configs
@@ -79,8 +87,24 @@ module RosettAi
79
87
  '.overcommit.yml', '.git-hooks',
80
88
  '.mdlrc', '.mdl_style.rb', '.yamllint', '.yardopts',
81
89
  '.project', '.rosett-ai',
90
+ # Dev-only documentation (not needed at runtime)
91
+ 'doc/api', 'doc/reference', 'doc/context', 'doc/issues',
92
+ 'doc/decisions', 'doc/changes', 'doc/design',
93
+ # Security advisories — internal, never ship publicly
94
+ 'doc/security',
95
+ # Design docs — carry internal WP refs, not runtime-required
96
+ 'conf/design',
97
+ # Behaviour rules — user-authored, must never ship in packages (sec_001/rule_073)
98
+ 'conf/behaviour',
99
+ # Peer review configs (dev-only)
100
+ 'conf/review',
82
101
  # AI assistant state — never ship in package
83
102
  '.claude', 'doc/claude-sessions',
103
+ # AI provenance file — dev-only provenance tracking, not runtime-required
104
+ '.ai-provenance.yml',
105
+ # Internal audit, operations, signing, and rule-audit documents
106
+ 'doc/audits', 'doc/operations', 'doc/signing',
107
+ 'doc/rules-audit-*.md',
84
108
  'cliff.toml', 'kitchen.yml', 'Rakefile'
85
109
  ].freeze
86
110
 
@@ -89,7 +113,7 @@ module RosettAi
89
113
  :validate, :clean, :prepare, :compile_ruby,
90
114
  :sync_app, :install_gems, :clean_build_artifacts,
91
115
  :install_wrapper,
92
- :install_config, :install_man, :install_build_info, :package
116
+ :install_config, :install_systemd, :install_man, :install_build_info, :rewrite_shebangs, :package
93
117
  ].freeze
94
118
 
95
119
  # Bundler/RubyGems environment variables that must be stripped
@@ -162,6 +186,9 @@ module RosettAi
162
186
  default: 'core',
163
187
  desc: 'Package variant (core, gtk4, qt6)'
164
188
 
189
+ # Build a .deb package for the specified variant.
190
+ #
191
+ # @return [void]
165
192
  def create
166
193
  unless RosettAi.context.rai_internal?
167
194
  raise ::Thor::Error,
@@ -181,8 +208,10 @@ module RosettAi
181
208
  run_stage(:clean_build_artifacts) { clean_build_artifacts! }
182
209
  run_stage(:install_wrapper) { install_wrapper! }
183
210
  run_stage(:install_config) { install_default_config! }
211
+ run_stage(:install_systemd) { install_systemd_unit! }
184
212
  run_stage(:install_man) { install_man_pages! }
185
213
  run_stage(:install_build_info) { install_build_info! }
214
+ run_stage(:rewrite_shebangs) { rewrite_shebangs! }
186
215
  run_stage(:package) { build_package! }
187
216
 
188
217
  print_build_summary(:success)
@@ -295,7 +324,7 @@ module RosettAi
295
324
  end
296
325
 
297
326
  def check_ronn_available!
298
- ronn_source = project_root.join('doc', 'man', 'rai.1.ronn')
327
+ ronn_source = project_root.join('doc', 'man', 'raictl.1.ronn')
299
328
  return unless ronn_source.exist?
300
329
 
301
330
  begin
@@ -415,13 +444,24 @@ module RosettAi
415
444
  # -- gem installation --------------------------------------------------
416
445
 
417
446
  def install_gems!
418
- spinner = build_spinner('Installing gems (deployment mode)...')
447
+ spinner = build_spinner('Installing gems into embedded gem home...')
448
+ ruby_abi = query_embedded_ruby('RbConfig::CONFIG["ruby_version"]')
449
+ gem_home = staging_embedded.join('lib', 'ruby', 'gems', ruby_abi)
450
+ FileUtils.mkdir_p(gem_home)
419
451
  embedded_bundle = staging_embedded.join('bin', 'bundle').to_s
420
452
 
421
- env = embedded_env
453
+ # Install runtime deps directly into the embedded Ruby's own gem home.
454
+ # The embedded ruby finds them by default (via GEM_HOME) with no wrapper
455
+ # env vars or bundler/setup at runtime — proving genuine self-containment.
456
+ env = clean_env.merge(
457
+ 'PATH' => "#{staging_embedded.join('bin')}:#{HERMETIC_SYSTEM_PATH}",
458
+ 'BUNDLE_GEMFILE' => staging_app.join('Gemfile').to_s,
459
+ 'GEM_HOME' => gem_home.to_s,
460
+ 'GEM_PATH' => gem_home.to_s,
461
+ 'LD_LIBRARY_PATH' => staging_embedded.join('lib').to_s
462
+ )
463
+
422
464
  cmds = [
423
- [env, embedded_bundle, 'config', 'set', '--local', 'deployment', 'true'],
424
- [env, embedded_bundle, 'config', 'set', '--local', 'path', 'vendor/bundle'],
425
465
  [env, embedded_bundle, 'config', 'set', '--local', 'without', 'development test build desktop engines'],
426
466
  [env, embedded_bundle, 'config', 'set', '--local', 'jobs', jobs_count.to_s],
427
467
  [env, embedded_bundle, 'install']
@@ -433,8 +473,8 @@ module RosettAi
433
473
  spinner.error(Rainbow(' failed').red)
434
474
  log_bundle_debug(env, embedded_bundle)
435
475
  abort_build(<<~MSG.chomp)
436
- Gem installation failed.
437
- The embedded Ruby bundler could not resolve or install gems.
476
+ Gem installation into embedded gem home failed.
477
+ The embedded Ruby bundler could not install gems into #{gem_home}.
438
478
  This usually means Bundler environment variables from the parent
439
479
  process leaked into the build. Check the build log for details.
440
480
  MSG
@@ -463,8 +503,8 @@ module RosettAi
463
503
  end
464
504
 
465
505
  def collect_build_artifacts
466
- Dir.glob(staging_app.join('vendor', 'bundle', '**', '{gem_make.out,mkmf.log}')) +
467
- Dir.glob(staging_app.join('vendor', 'bundle', '**', 'ext', '**', 'Makefile'))
506
+ Dir.glob(staging_embedded.join('lib', 'ruby', 'gems', '**', '{gem_make.out,mkmf.log}')) +
507
+ Dir.glob(staging_embedded.join('lib', 'ruby', 'gems', '**', 'ext', '**', 'Makefile'))
468
508
  end
469
509
 
470
510
  def remove_artifact(path)
@@ -494,15 +534,6 @@ module RosettAi
494
534
  # 1. Unsets all Bundler/RubyGems vars inherited from `bundle exec`
495
535
  # 2. Sets PATH to prefer the embedded Ruby
496
536
  # 3. Points BUNDLE_GEMFILE at the staging app's Gemfile
497
- def embedded_env
498
- env = clean_env.merge(
499
- 'PATH' => "#{staging_embedded.join('bin')}:#{HERMETIC_SYSTEM_PATH}",
500
- 'BUNDLE_GEMFILE' => staging_app.join('Gemfile').to_s
501
- )
502
- log_to_file("embedded_env: #{env.inspect}")
503
- env
504
- end
505
-
506
537
  # Strips all Bundler/RubyGems variables. When passed to system(),
507
538
  # nil values cause the variable to be unset in the child process.
508
539
  def clean_env
@@ -560,10 +591,25 @@ module RosettAi
560
591
  log_verbose("Installed conffile to #{etc_dir.join('settings.json')}")
561
592
  end
562
593
 
594
+ # -- systemd -----------------------------------------------------------
595
+
596
+ def install_systemd_unit!
597
+ systemd_src = packaging_dir.join('lib', 'systemd')
598
+ return log_verbose('No systemd unit source found — skipping') unless systemd_src.directory?
599
+
600
+ systemd_dest = staging_install.join('lib', 'systemd')
601
+ FileUtils.mkdir_p(systemd_dest)
602
+
603
+ Dir.glob(systemd_src.join('*').to_s).each do |src_path|
604
+ FileUtils.cp_r(src_path, systemd_dest)
605
+ log_verbose("Installed #{File.basename(src_path)} to #{systemd_dest}")
606
+ end
607
+ end
608
+
563
609
  # -- man pages ---------------------------------------------------------
564
610
 
565
611
  def install_man_pages!
566
- ronn_source = project_root.join('doc', 'man', 'rai.1.ronn')
612
+ ronn_source = project_root.join('doc', 'man', 'raictl.1.ronn')
567
613
  return log_verbose('No man page source found — skipping') unless ronn_source.exist?
568
614
 
569
615
  man_dir = staging_dir.join('usr', 'share', 'man', 'man1')
@@ -590,6 +636,39 @@ module RosettAi
590
636
  log_verbose("Installed build info to #{dest}")
591
637
  end
592
638
 
639
+ # -- shebang rewrite ---------------------------------------------------
640
+
641
+ # ruby-build compiles with the staging prefix as --prefix, so scripts
642
+ # in embedded/bin/ (gem, bundle, rake, irb, …) carry the build-time
643
+ # staging path in their shebang. Rewrite all ruby-shebang scripts to
644
+ # the runtime path AFTER gem installation (which still needs the
645
+ # staging shebang to run bundle) and BEFORE packaging.
646
+ def rewrite_shebangs!
647
+ spinner = build_spinner('Rewriting embedded bin shebangs...')
648
+ bin_dir = staging_embedded.join('bin')
649
+ count = count_shebang_rewrites(bin_dir, '#!/opt/rosett-ai/embedded/bin/ruby')
650
+ log_verbose("Rewrote #{count} shebang(s) in #{bin_dir}")
651
+ spinner.success(Rainbow(" done (#{count} scripts)").green)
652
+ end
653
+
654
+ def count_shebang_rewrites(bin_dir, target)
655
+ return 0 unless bin_dir.directory?
656
+
657
+ bin_dir.each_child.count { |path| !path.directory? && rewrite_ruby_shebang!(path, target) }
658
+ end
659
+
660
+ def rewrite_ruby_shebang!(path, target)
661
+ content = path.binread
662
+ return unless content.start_with?('#!')
663
+
664
+ nl = content.index("\n") || content.length
665
+ first_line = content[0, nl]
666
+ return unless first_line.include?('ruby') && first_line.strip != target
667
+
668
+ path.binwrite("#{target}#{content[nl..]}")
669
+ true
670
+ end
671
+
593
672
  def build_info_content
594
673
  <<~INFO
595
674
  version=#{RosettAi::VERSION}
@@ -741,8 +820,10 @@ module RosettAi
741
820
  install_gems: 'Install gems (deployment)',
742
821
  install_wrapper: 'Install wrapper script',
743
822
  install_config: 'Install default config',
823
+ install_systemd: 'Install systemd unit',
744
824
  install_man: 'Install man pages',
745
825
  install_build_info: 'Install build info',
826
+ rewrite_shebangs: 'Rewrite embedded bin shebangs',
746
827
  package: 'Build .deb with fpm'
747
828
  }.fetch(stage, stage.to_s)
748
829
  end
@@ -848,6 +929,7 @@ module RosettAi
848
929
 
849
930
  default_task :create
850
931
 
932
+ # @return [String] Base installation path for engine plugins.
851
933
  INSTALL_BASE = '/opt/rosett-ai/embedded/lib/ruby/gems'
852
934
 
853
935
  desc 'create', 'Build a .deb package for an rosett-ai engine plugin'
@@ -885,6 +967,9 @@ module RosettAi
885
967
  default: 1,
886
968
  desc: 'Package build iteration (release number)'
887
969
 
970
+ # Build a .deb package for an engine plugin.
971
+ #
972
+ # @return [void]
888
973
  def create
889
974
  resolve_engine_dir!
890
975
  detect_gemspec!
@@ -30,6 +30,7 @@ module RosettAi
30
30
  --simulate: Dry run with diffs, no file writes
31
31
  --vendor: Write compile.lock.yml lockfile
32
32
  --engine NAME: Compile for specific engine
33
+ --scope SCOPE: Compile scope (global/project/auto)
33
34
  --strict: Treat warnings as errors
34
35
  --locales: Compile locale files to gettext/Qt formats
35
36
 
@@ -39,6 +40,8 @@ module RosettAi
39
40
 
40
41
  raictl compile
41
42
  raictl compile --verbose --simulate
43
+ raictl compile --scope global
44
+ raictl compile --scope project
42
45
  raictl compile --engine generic --strict
43
46
  raictl compile --vendor
44
47
  raictl compile --locales
@@ -73,11 +76,18 @@ module RosettAi
73
76
  type: :boolean,
74
77
  default: false,
75
78
  desc: 'Treat warnings as errors'
79
+ method_option :scope,
80
+ type: :string,
81
+ default: 'auto',
82
+ enum: ['global', 'project', 'auto'],
83
+ desc: 'Compile scope: global (~/.claude/rules/), project (.claude/rules/), or auto-detect'
76
84
  method_option :locales,
77
85
  type: :boolean,
78
86
  default: false,
79
87
  desc: 'Compile locale files to gettext and Qt formats'
80
88
 
89
+ # Generate the compiled output.
90
+ # @return [void]
81
91
  def generate
82
92
  if options[:locales]
83
93
  compile_locales
@@ -153,8 +163,17 @@ module RosettAi
153
163
  end
154
164
 
155
165
  def detect_compile_scope
156
- ctx = RosettAi.context
157
- ctx.project? && !ctx.rai_internal? ? :project : :global
166
+ case options[:scope]
167
+ when 'global'
168
+ :global
169
+ when 'project'
170
+ raise RosettAi::CompileError, t('compile.scope_project_no_project') unless RosettAi.context.project?
171
+
172
+ :project
173
+ else # 'auto'
174
+ ctx = RosettAi.context
175
+ ctx.project? && !ctx.rai_internal? ? :project : :global
176
+ end
158
177
  end
159
178
 
160
179
  def check_xdg_migration
@@ -236,30 +255,18 @@ module RosettAi
236
255
  RosettAi.paths.rules_dir
237
256
  end
238
257
 
239
- # Resolves the target directory for a compiled output based on its
240
- # declared scope. Falls back to the pipeline's target_dir when no
241
- # scope is declared or the behaviour's scope matches the pipeline's.
258
+ # Resolves the target directory for a compiled output.
242
259
  #
243
- # Scope routing:
244
- # global/personal ~/.claude/rules/ (global rules dir)
245
- # project → .claude/rules/ (project-local)
246
- # nil → pipeline default (current behaviour)
247
- def resolve_scoped_target_dir(compiler, info)
248
- declared_scope = info[:scope]
249
- return compiler.target_dir unless declared_scope
250
-
251
- case declared_scope
252
- when 'project'
253
- if RosettAi.context.project?
254
- RosettAi.context.project_root.join('.claude', 'rules')
255
- else
256
- compiler.target_dir
257
- end
258
- when 'global', 'personal'
259
- RosettAi.paths.rules_dir
260
- else
261
- compiler.target_dir
262
- end
260
+ # Always returns the pipeline's target_dir to prevent cross-directory
261
+ # writes. The pipeline scope (global vs project) already determines
262
+ # the correct output directory. Per-file scope metadata is preserved
263
+ # in lockfile output but does not affect write location.
264
+ #
265
+ # Previously, per-file scope: global/personal routed to
266
+ # ~/.claude/rules/ even during project-scope compilation, causing
267
+ # Claude Code to load duplicate rules from both directories.
268
+ def resolve_scoped_target_dir(compiler, _info)
269
+ compiler.target_dir
263
270
  end
264
271
 
265
272
  def compile_locales
@@ -296,10 +303,17 @@ module RosettAi
296
303
 
297
304
  compiled.each do |filename, info|
298
305
  target_dir = resolve_scoped_target_dir(compiler, info)
299
- target_path = target_dir.join(filename)
300
306
  content = info[:content]
301
307
  new_checksum = compiler.checksum(content)
302
308
 
309
+ if deduplicate_against_global?(compiler, target_dir, filename, new_checksum)
310
+ cleanup_stale_project_copy(target_dir, filename) unless options[:simulate]
311
+ results[:skipped] << filename
312
+ log_verbose(t('compile.deduplicated_detail', filename: filename))
313
+ next
314
+ end
315
+
316
+ target_path = target_dir.join(filename)
303
317
  ctx = CompileFileContext.new(
304
318
  target_path: target_path, content: content, checksum: new_checksum, filename: filename
305
319
  )
@@ -313,6 +327,32 @@ module RosettAi
313
327
  results
314
328
  end
315
329
 
330
+ # Checks whether a file should be deduplicated against the global
331
+ # rules directory. In project scope, if a compiled file is identical
332
+ # to its global counterpart in ~/.claude/rules/, writing it to the
333
+ # project .claude/rules/ would cause Claude Code to load it twice.
334
+ #
335
+ # @return [Boolean] true if the file should be skipped
336
+ def deduplicate_against_global?(compiler, target_dir, filename, checksum)
337
+ return false unless compiler.scope == :project
338
+
339
+ global_dir = RosettAi.paths.rules_dir
340
+ return false if target_dir.to_s == global_dir.to_s
341
+
342
+ global_file = global_dir.join(filename)
343
+ return false unless global_file.exist?
344
+
345
+ global_checksum = compiler.checksum(File.read(global_file, encoding: 'utf-8'))
346
+ checksum == global_checksum
347
+ end
348
+
349
+ # Removes a stale project-scope copy that was previously compiled
350
+ # but is now deduplicated against the global version.
351
+ def cleanup_stale_project_copy(target_dir, filename)
352
+ stale_path = target_dir.join(filename)
353
+ File.delete(stale_path) if stale_path.exist?
354
+ end
355
+
316
356
  def handle_simulate(compiler, ctx, results)
317
357
  if File.exist?(ctx.target_path)
318
358
  diff_output = compiler.diff(ctx.target_path, ctx.content)
@@ -366,7 +406,7 @@ module RosettAi
366
406
  def remove_orphans(compiler, compiled)
367
407
  # Guard: when compile produced no output (e.g. empty project conf/),
368
408
  # every managed file in target_dir would appear orphaned. Removing
369
- # them would silently destroy all compiled rules. See WP#712.
409
+ # them would silently destroy all compiled rules.
370
410
  return if compiled.empty?
371
411
 
372
412
  orphans = compiler.orphaned_files(compiled.keys)
@@ -389,11 +429,15 @@ module RosettAi
389
429
  end
390
430
 
391
431
  def print_summary(results)
392
- rows = []
393
- rows << [t('compile.created'), results[:created].size] unless results[:created].empty?
394
- rows << [t('compile.updated'), results[:updated].size] unless results[:updated].empty?
395
- rows << [t('compile.unchanged', filename: ''), results[:unchanged].size] unless results[:unchanged].empty?
396
-
432
+ label_map = {
433
+ created: t('compile.created'),
434
+ updated: t('compile.updated'),
435
+ unchanged: t('compile.unchanged', filename: ''),
436
+ skipped: t('compile.deduplicated')
437
+ }
438
+ rows = label_map.filter_map do |key, label|
439
+ [label, results[key].size] unless results[key].empty?
440
+ end
397
441
  return if rows.empty?
398
442
 
399
443
  puts
@@ -15,6 +15,7 @@ module RosettAi
15
15
  # @author hugo
16
16
  # @author claude
17
17
  class Completion < ::Thor
18
+ # @return [Hash] Shell-specific completion installation paths.
18
19
  INSTALL_PATHS = {
19
20
  'bash' => '/usr/share/bash-completion/completions/rai',
20
21
  'zsh' => '/usr/share/zsh/vendor-completions/_rai',
@@ -29,6 +30,8 @@ module RosettAi
29
30
 
30
31
  Related: completion zsh, completion fish, completion install
31
32
  LONGDESC
33
+ # Generate bash shell completion script.
34
+ # @return [void]
32
35
  def bash
33
36
  output_completion('bash')
34
37
  end
@@ -41,6 +44,8 @@ module RosettAi
41
44
 
42
45
  Related: completion bash, completion fish, completion install
43
46
  LONGDESC
47
+ # Generate zsh shell completion script.
48
+ # @return [void]
44
49
  def zsh
45
50
  output_completion('zsh')
46
51
  end
@@ -53,6 +58,8 @@ module RosettAi
53
58
 
54
59
  Related: completion bash, completion zsh, completion install
55
60
  LONGDESC
61
+ # Generate fish shell completion script.
62
+ # @return [void]
56
63
  def fish
57
64
  output_completion('fish')
58
65
  end
@@ -78,6 +85,8 @@ module RosettAi
78
85
  LONGDESC
79
86
  method_option :shell, type: :string, default: nil,
80
87
  desc: 'Shell to install for (auto-detected from $SHELL)'
88
+ # Install the component.
89
+ # @return [void]
81
90
  def install
82
91
  shell = detect_shell
83
92
  generator = RosettAi::Completion::Generator.new
@@ -46,6 +46,8 @@ module RosettAi
46
46
  desc: 'Run SPDX header checks only'
47
47
  method_option :format, type: :string, enum: ['table', 'json'], default: 'table',
48
48
  desc: 'Output format'
49
+ # Run the check and return results.
50
+ # @return [Hash]
49
51
  def check
50
52
  checks = selected_checks
51
53
  results = RosettAi::Comply::Runner.new(
@@ -11,7 +11,7 @@ module RosettAi
11
11
  module Thor
12
12
  module Tasks
13
13
  # Thor task for compiling engine configuration YAML to settings JSON.
14
- class Config < ::Thor
14
+ class Config < ::Thor # rubocop:disable Metrics/ClassLength
15
15
  default_task :compile
16
16
 
17
17
  desc 'show SCOPE', 'Display compiled configuration for a scope with secrets masked'
@@ -36,6 +36,10 @@ module RosettAi
36
36
  method_option :engine, type: :string, default: nil,
37
37
  desc: 'Engine name (default: configured default)'
38
38
 
39
+ # Display detailed information for the named item.
40
+ #
41
+ # @param scope [Object] the scope
42
+ # @return [void]
39
43
  def show(scope)
40
44
  scope = RosettAi::TextSanitizer.normalize_nfc(scope)
41
45
  compiler = resolve_config_compiler(simulate: true, masking: true)
@@ -58,6 +62,12 @@ module RosettAi
58
62
  --simulate: Dry run without writing files
59
63
  --scope NAME: Compile only one scope (managed, user, project, local)
60
64
  --engine NAME: Compile for specific engine
65
+ --strict: Replace target entirely — do not preserve uncategorized entries
66
+
67
+ The default (non-strict) compile MERGES with the existing target file,
68
+ preserving any entries not present in the source YAML scopes and emitting
69
+ a warning for each. Use --strict to restore the old replace-all behaviour.
70
+ Run `raictl backfill-user-yml` to capture preserved entries into source.
61
71
 
62
72
  Exit codes: 1 if any scope fails to compile
63
73
 
@@ -66,8 +76,9 @@ module RosettAi
66
76
  raictl config compile
67
77
  raictl config compile --verbose --simulate
68
78
  raictl config compile --scope managed --engine claude
79
+ raictl config compile --strict
69
80
 
70
- Related commands: raictl config show, rai compile
81
+ Related commands: raictl config show, raictl backfill-user-yml, rai compile
71
82
  LONGDESC
72
83
  method_option :verbose, type: :boolean, default: false,
73
84
  desc: 'Show detailed output'
@@ -77,9 +88,13 @@ module RosettAi
77
88
  desc: 'Compile specific scope only (managed, user, project, local)'
78
89
  method_option :engine, type: :string, default: nil,
79
90
  desc: 'Engine name (default: configured default)'
91
+ method_option :strict, type: :boolean, default: false,
92
+ desc: 'Replace target entirely — do not preserve uncategorized entries'
80
93
 
94
+ # Compile the configuration.
95
+ # @return [void]
81
96
  def compile
82
- compiler = resolve_config_compiler(simulate: options[:simulate])
97
+ compiler = resolve_config_compiler(simulate: options[:simulate], strict: options[:strict])
83
98
 
84
99
  results = if options[:scope]
85
100
  scope_path = find_scope_file(options[:scope], compiler)
@@ -110,7 +125,7 @@ module RosettAi
110
125
  options[:engine] || RosettAi.rai_config.default_engine
111
126
  end
112
127
 
113
- def resolve_config_compiler(simulate: false, masking: false)
128
+ def resolve_config_compiler(simulate: false, masking: false, strict: false)
114
129
  engine_name = resolve_engine_name
115
130
 
116
131
  unless RosettAi::Plugins::Registry.registered?(:engine, engine_name)
@@ -128,7 +143,8 @@ module RosettAi
128
143
  end
129
144
 
130
145
  secret_resolver = masking ? RosettAi::Config::MaskingSecretResolver.new : nil
131
- plugin.config_compiler_class.new(simulate: simulate, secret_resolver: secret_resolver)
146
+ plugin.config_compiler_class.new(simulate: simulate, secret_resolver: secret_resolver,
147
+ strict: strict)
132
148
  end
133
149
 
134
150
  def engine_hint(available, engine_name)
@@ -32,6 +32,8 @@ module RosettAi
32
32
  LONGDESC
33
33
  method_option :available, type: :boolean, default: false,
34
34
  desc: 'Show all available packs with tier labels'
35
+ # List available items.
36
+ # @return [void]
35
37
  def list
36
38
  if options[:available]
37
39
  list_available
@@ -53,6 +55,10 @@ module RosettAi
53
55
 
54
56
  Related commands: raictl content list --available, rai license status
55
57
  LONGDESC
58
+ # Install the component.
59
+ #
60
+ # @param pack_name [Object] the pack name
61
+ # @return [void]
56
62
  def install(pack_name)
57
63
  result = validate_license!
58
64
  tier = result[:tier]
@@ -85,6 +91,8 @@ module RosettAi
85
91
 
86
92
  Related commands: raictl content list, rai license status
87
93
  LONGDESC
94
+ # Update to the latest version.
95
+ # @return [void]
88
96
  def update
89
97
  validate_license!
90
98
  registry = RosettAi::Content::PackRegistry.new
@@ -21,14 +21,19 @@ module RosettAi
21
21
  # @author hugo
22
22
  # @author claude
23
23
  class DBus < ::Thor
24
+ # @return [Integer] Exit code for successful D-Bus operation.
24
25
  EXIT_SUCCESS = 0
26
+ # @return [Integer] Exit code for D-Bus connection failure.
25
27
  EXIT_DBUS_FAILURE = 1
28
+ # @return [Integer] Exit code for missing D-Bus dependency.
26
29
  EXIT_MISSING_DEPENDENCY = 5
27
30
 
31
+ # @return [Array] Environment variables checked for compositor detection.
28
32
  ENV_VARS = [
29
33
  'SWAYSOCK', 'I3SOCK', 'HYPRLAND_INSTANCE_SIGNATURE', 'DISPLAY', 'WAYLAND_DISPLAY', 'XDG_RUNTIME_DIR', 'XDG_SESSION_TYPE'
30
34
  ].freeze
31
35
 
36
+ # @return [Hash] Maps compositor names to focus adapter classes.
32
37
  ADAPTER_MAP = {
33
38
  sway: 'I3Adapter (i3ipc)',
34
39
  i3: 'I3Adapter (i3ipc)',
@@ -54,6 +59,8 @@ module RosettAi
54
59
 
55
60
  Related commands: raictl dbus start, rai dbus detect
56
61
  LONGDESC
62
+ # Return the current status.
63
+ # @return [String]
57
64
  def status
58
65
  require_desktop_gems!
59
66
  check_dbus_availability
@@ -90,6 +97,8 @@ module RosettAi
90
97
  default: false,
91
98
  aliases: '-d',
92
99
  desc: 'Run as daemon (background)'
100
+ # Start the adapter or service.
101
+ # @return [void]
93
102
  def start
94
103
  require_desktop_gems!
95
104
  check_dbus_availability
@@ -121,6 +130,8 @@ module RosettAi
121
130
 
122
131
  Related commands: raictl dbus start, rai dbus status
123
132
  LONGDESC
133
+ # Stop the adapter or service.
134
+ # @return [void]
124
135
  def stop
125
136
  require_desktop_gems!
126
137
  unless service_running?
@@ -145,6 +156,8 @@ module RosettAi
145
156
 
146
157
  Related commands: raictl dbus status, rai dbus start
147
158
  LONGDESC
159
+ # Detect available engines on the system.
160
+ # @return [Array<Hash>]
148
161
  def detect
149
162
  require_desktop_gems!
150
163
  compositor = RosettAi::DBus::CompositorDetector.detect
@@ -87,6 +87,8 @@ module RosettAi
87
87
 
88
88
  Related commands: raictl design show, rai design validate
89
89
  LONGDESC
90
+ # List available items.
91
+ # @return [void]
90
92
  def list
91
93
  files = design_files
92
94
  if files.empty?
@@ -29,6 +29,8 @@ module RosettAi
29
29
 
30
30
  Related commands: raictl desktop status, rai dbus start
31
31
  LONGDESC
32
+ # Launch the GTK4 desktop application.
33
+ # @return [void]
32
34
  def gtk4
33
35
  require_desktop_gems!
34
36
  unless RosettAi::Desktop::Gtk4App.available?
@@ -52,6 +54,8 @@ module RosettAi
52
54
 
53
55
  Related commands: raictl desktop gtk4, rai dbus status
54
56
  LONGDESC
57
+ # Return the current status.
58
+ # @return [String]
55
59
  def status
56
60
  require_desktop_gems!
57
61
  gtk4_available = RosettAi::Desktop::Gtk4App.available?