rosett-ai 1.3.3

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 (527) hide show
  1. checksums.yaml +7 -0
  2. data/.ai-provenance.yml +119 -0
  3. data/.debride_whitelist +186 -0
  4. data/.fasterer.yml +29 -0
  5. data/.mdl_style.rb +10 -0
  6. data/.mdlrc +3 -0
  7. data/.mutant.yml +49 -0
  8. data/.namespace-allowlist +42 -0
  9. data/.reek.yml +1040 -0
  10. data/.rosett-ai/config.yml +3 -0
  11. data/.rspec +5 -0
  12. data/.rubocop.yml +380 -0
  13. data/.ruby-version +1 -0
  14. data/.yamllint +51 -0
  15. data/.yardopts +12 -0
  16. data/AI-DISCLOSURE.md +48 -0
  17. data/CHANGELOG.md +519 -0
  18. data/CLAUDE.md +141 -0
  19. data/CONTRIBUTING.md +734 -0
  20. data/INSTALL.md +154 -0
  21. data/LICENSE +674 -0
  22. data/LICENSE.md +675 -0
  23. data/QUICKSTART.md +73 -0
  24. data/README.md +366 -0
  25. data/Rakefile +200 -0
  26. data/SECURITY.md +114 -0
  27. data/bin/rai +1 -0
  28. data/cliff.toml +52 -0
  29. data/conf/adopt_redactions.yml +8 -0
  30. data/conf/behaviour/.gitkeep +0 -0
  31. data/conf/compliance/cra_rules.yml +25 -0
  32. data/conf/compliance/license_rules.yml +20 -0
  33. data/conf/design/aaif_alignment.yml +181 -0
  34. data/conf/design/ab_testing.yml +172 -0
  35. data/conf/design/accessibility.yml +84 -0
  36. data/conf/design/ai_authorship.yml +210 -0
  37. data/conf/design/ai_provenance.yml +224 -0
  38. data/conf/design/ai_tool_configuration.yml +207 -0
  39. data/conf/design/architecture.yml +139 -0
  40. data/conf/design/autocompletion.yml +115 -0
  41. data/conf/design/backward_compatibility.yml +112 -0
  42. data/conf/design/behaviour_composition.yml +246 -0
  43. data/conf/design/build_rake_extraction.yml +57 -0
  44. data/conf/design/ci_pipeline.yml +100 -0
  45. data/conf/design/claude_code_configuration.yml +157 -0
  46. data/conf/design/compiler.yml +128 -0
  47. data/conf/design/comply.yml +153 -0
  48. data/conf/design/content_packs.yml +84 -0
  49. data/conf/design/desktop_integration.yml +289 -0
  50. data/conf/design/distribution.yml +216 -0
  51. data/conf/design/doctor.yml +184 -0
  52. data/conf/design/documentation.yml +152 -0
  53. data/conf/design/engine_architecture.yml +257 -0
  54. data/conf/design/error_handling.yml +103 -0
  55. data/conf/design/feature_flags.yml +142 -0
  56. data/conf/design/git_hooks.yml +165 -0
  57. data/conf/design/gui_plugins.yml +475 -0
  58. data/conf/design/i18n.yml +84 -0
  59. data/conf/design/integration_testing.yml +56 -0
  60. data/conf/design/licensing_system.yml +88 -0
  61. data/conf/design/lifecycle_management.yml +208 -0
  62. data/conf/design/mcp_integration.yml +207 -0
  63. data/conf/design/mcp_settings.yml +126 -0
  64. data/conf/design/migration.yml +56 -0
  65. data/conf/design/monitoring_observability.yml +194 -0
  66. data/conf/design/namespace_cleanup.yml +145 -0
  67. data/conf/design/plugin_test_segregation.yml +145 -0
  68. data/conf/design/policy_management.yml +229 -0
  69. data/conf/design/project_management.yml +183 -0
  70. data/conf/design/rai_mcp_asset_discovery.yml +164 -0
  71. data/conf/design/rai_mcp_server.yml +605 -0
  72. data/conf/design/release_management.yml +117 -0
  73. data/conf/design/retrofit.yml +199 -0
  74. data/conf/design/retrospective_analyzer.yml +79 -0
  75. data/conf/design/scope_hierarchy.yml +352 -0
  76. data/conf/design/security.yml +115 -0
  77. data/conf/design/session_retrospective.yml +85 -0
  78. data/conf/design/smart_ui_feedback.yml +89 -0
  79. data/conf/design/structured_logging.yml +148 -0
  80. data/conf/design/styles.yml +123 -0
  81. data/conf/design/test_peer_review.yml +89 -0
  82. data/conf/design/testing.yml +136 -0
  83. data/conf/design/threat_model.yml +108 -0
  84. data/conf/design/ui_framework.yml +111 -0
  85. data/conf/design/usage_optimization.yml +122 -0
  86. data/conf/design/version_management.yml +60 -0
  87. data/conf/design/workflow.yml +227 -0
  88. data/conf/mcp/server_defaults.yml +42 -0
  89. data/conf/mcp/trust.yml +21 -0
  90. data/conf/packaging/core.yml +12 -0
  91. data/conf/packaging/gtk4.yml +11 -0
  92. data/conf/packaging/qt6.yml +11 -0
  93. data/conf/policy/default_deny_list.yml +197 -0
  94. data/conf/review/cli-command-audit.yml +857 -0
  95. data/conf/review/design-docs.yml +1064 -0
  96. data/conf/review/design-questionnaire.yml +153 -0
  97. data/conf/review/questionnaire.yml +146 -0
  98. data/conf/review/rosett-ai-core.yml +2919 -0
  99. data/conf/schemas/ai_config_schema.json +73 -0
  100. data/conf/schemas/behaviour_schema.json +132 -0
  101. data/conf/schemas/compliance_rule_schema.json +63 -0
  102. data/conf/schemas/content_pack_manifest_schema.json +51 -0
  103. data/conf/schemas/design_schema.json +210 -0
  104. data/conf/schemas/engine_manifest_schema.json +144 -0
  105. data/conf/schemas/lockfile_schema.json +74 -0
  106. data/conf/schemas/mcp_server_schema.json +48 -0
  107. data/conf/schemas/packaging_schema.json +70 -0
  108. data/conf/schemas/policy_schema.json +85 -0
  109. data/conf/schemas/provenance_schema.json +84 -0
  110. data/conf/schemas/rai_config_schema.json +56 -0
  111. data/conf/schemas/rai_project_schema.json +20 -0
  112. data/conf/schemas/scope_hierarchy_schema.json +49 -0
  113. data/conf/schemas/target_schema.json +67 -0
  114. data/conf/schemas/tooling_schema.json +65 -0
  115. data/conf/schemas/workflow_schema.json +112 -0
  116. data/conf/targets/agents_md.yml +17 -0
  117. data/conf/targets/claude.yml +12 -0
  118. data/conf/tooling/tools.yml +58 -0
  119. data/dist/rosett-ai-mcp.service +48 -0
  120. data/dist/rosett-ai-mcp.yml.default +45 -0
  121. data/doc/AAIF_POSITIONING.md +58 -0
  122. data/doc/ADOPT.md +224 -0
  123. data/doc/AI_PROVENANCE.md +139 -0
  124. data/doc/ARCHITECTURE.md +920 -0
  125. data/doc/BEHAVIOUR.md +409 -0
  126. data/doc/BUILD.md +138 -0
  127. data/doc/CI_CD_RECIPES.md +171 -0
  128. data/doc/CLAUDE_SESSIONS_MOVED.md +16 -0
  129. data/doc/COMMAND_ANALYSIS.md +229 -0
  130. data/doc/CONFIGURATION.md +281 -0
  131. data/doc/DESIGN_AUDIT.md +235 -0
  132. data/doc/DESIGN_PEER_REVIEW.md +771 -0
  133. data/doc/DESKTOP.md +447 -0
  134. data/doc/ENGINES.md +567 -0
  135. data/doc/ENGINE_DEVELOPMENT_GUIDE.md +417 -0
  136. data/doc/FEATURE_AUDIT.md +218 -0
  137. data/doc/IMPLEMENTATION_PLAN.md +669 -0
  138. data/doc/INCIDENT_REPORT_2026-02-02.md +251 -0
  139. data/doc/MIGRATION_GUIDE.md +88 -0
  140. data/doc/PACKAGING.md +232 -0
  141. data/doc/PROJECT_DASHBOARD.md +153 -0
  142. data/doc/PULP_DEPLOYMENT.md +164 -0
  143. data/doc/QUALITY_FIX_SUMMARY.md +110 -0
  144. data/doc/QUICK_START.md +162 -0
  145. data/doc/REEK_CONFIGURATION.md +166 -0
  146. data/doc/REFERENCE.md +253 -0
  147. data/doc/REFERENCES.md +324 -0
  148. data/doc/SECURITY_REVIEW_CHECKLIST.md +72 -0
  149. data/doc/SESSION_2026-02-28_GTK4_HARDENING.md +359 -0
  150. data/doc/SETUP.md +202 -0
  151. data/doc/TEST_PEER_REVIEW.md +152 -0
  152. data/doc/THREAT_MODEL.md +230 -0
  153. data/doc/USAGE.md +545 -0
  154. data/doc/USER_MANUAL.md +585 -0
  155. data/doc/ai_test_review_checklist.md +110 -0
  156. data/doc/changes/2026-02-18-packaging-fpm.md +155 -0
  157. data/doc/changes/2026-02-19-testing-infrastructure.md +221 -0
  158. data/doc/changes/2026-02-20-security-implementation.md +281 -0
  159. data/doc/changes/2026-02-20-styles-implementation.md +220 -0
  160. data/doc/changes/2026-02-21-architecture-completion.md +95 -0
  161. data/doc/changes/2026-02-21-architecture-ui-layer.md +253 -0
  162. data/doc/changes/2026-02-21-cc-config-implementation.md +108 -0
  163. data/doc/changes/2026-02-21-ci-pipeline-implementation.md +214 -0
  164. data/doc/changes/2026-02-21-compiler-multi-target-pipeline.md +241 -0
  165. data/doc/changes/2026-02-21-config-design-show-commands.md +61 -0
  166. data/doc/changes/2026-02-21-design-implementation-overview.md +455 -0
  167. data/doc/changes/2026-02-21-lifecycle-management.md +196 -0
  168. data/doc/changes/2026-02-21-path-resolver.md +128 -0
  169. data/doc/changes/2026-02-24-ci-tmpdir-mutant-fetch.md +45 -0
  170. data/doc/changes/2026-03-01-ci-bundler-strategy.md +120 -0
  171. data/doc/changes/2026-03-20-security-hardening-phase2.md +163 -0
  172. data/doc/context/SESSION-HANDOFF.md +69 -0
  173. data/doc/context/ai-engine-usage-trends-2026.md +80 -0
  174. data/doc/context/plan-pluggable-engines.md +590 -0
  175. data/doc/decisions/001-flog-deferred.md +32 -0
  176. data/doc/decisions/002-path-resolution-strategy.md +158 -0
  177. data/doc/decisions/003-ui-adapter-selection.md +193 -0
  178. data/doc/decisions/004-design-document-validation.md +179 -0
  179. data/doc/decisions/005-package-splitting-strategy.md +200 -0
  180. data/doc/decisions/006-multi-engine-architecture.md +147 -0
  181. data/doc/decisions/007-engine-agnostic-pivot.md +219 -0
  182. data/doc/decisions/008-ci-bundler-strategy.md +129 -0
  183. data/doc/decisions/009-core-only-v1-release.md +60 -0
  184. data/doc/decisions/010-engine-debian-packaging.md +66 -0
  185. data/doc/decisions/011-context-aware-cli.md +71 -0
  186. data/doc/dependency_decisions.yml +247 -0
  187. data/doc/issues/001-wrapper-missing-environment-variables.md +197 -0
  188. data/doc/issues/002-embedded-ruby-wrong-prefix.md +217 -0
  189. data/doc/issues/003-smoke-test-false-positive.md +127 -0
  190. data/doc/issues/004-market-research-design-updates.md +109 -0
  191. data/doc/issues/005-compile-scope-coexistence.md +161 -0
  192. data/doc/locales/.gitkeep +0 -0
  193. data/doc/man/rai.1.ronn +505 -0
  194. data/doc/operations/packaging.md +133 -0
  195. data/doc/operations/rosett-ai-release.md +65 -0
  196. data/doc/reference/error-catalog.md +107 -0
  197. data/doc/reference/rosett-ai-technical-reference.pdf +0 -0
  198. data/doc/reference/src/Pictures/cover.jpg +0 -0
  199. data/doc/reference/src/Pictures/head1.jpg +0 -0
  200. data/doc/reference/src/Pictures/head2.jpg +0 -0
  201. data/doc/reference/src/Pictures/head3.jpg +0 -0
  202. data/doc/reference/src/Pictures/head4.jpg +0 -0
  203. data/doc/reference/src/Pictures/head5.jpg +0 -0
  204. data/doc/reference/src/Pictures/head6.jpg +0 -0
  205. data/doc/reference/src/Pictures/head7.jpg +0 -0
  206. data/doc/reference/src/Pictures/head8.jpg +0 -0
  207. data/doc/reference/src/StyleInd.ist +4 -0
  208. data/doc/reference/src/bibliography.bib +79 -0
  209. data/doc/reference/src/main.tex +1288 -0
  210. data/doc/reference/src/structure.tex +303 -0
  211. data/doc/rosett-ai-bookmarks.html +301 -0
  212. data/kitchen.yml +46 -0
  213. data/lib/rosett_ai/adopter/executor_resolver.rb +77 -0
  214. data/lib/rosett_ai/adopter/local_analysis_collector.rb +154 -0
  215. data/lib/rosett_ai/adopter/rule_adopter.rb +254 -0
  216. data/lib/rosett_ai/ai_config/config_compiler.rb +111 -0
  217. data/lib/rosett_ai/ai_config/context_window.rb +55 -0
  218. data/lib/rosett_ai/ai_config/cost_controls.rb +44 -0
  219. data/lib/rosett_ai/ai_config/fallback_chain.rb +64 -0
  220. data/lib/rosett_ai/ai_config/model_router.rb +121 -0
  221. data/lib/rosett_ai/ai_config/validator.rb +45 -0
  222. data/lib/rosett_ai/authorship/attribution_compiler.rb +99 -0
  223. data/lib/rosett_ai/authorship/disclosure_policy.rb +81 -0
  224. data/lib/rosett_ai/authorship/review_validator.rb +39 -0
  225. data/lib/rosett_ai/authorship/trailer_generator.rb +88 -0
  226. data/lib/rosett_ai/backup/compressor.rb +180 -0
  227. data/lib/rosett_ai/backup/destination.rb +91 -0
  228. data/lib/rosett_ai/behaviour/manager.rb +156 -0
  229. data/lib/rosett_ai/compiler/backend.rb +86 -0
  230. data/lib/rosett_ai/compiler/backends/agents_md_backend.rb +80 -0
  231. data/lib/rosett_ai/compiler/backends/claude_backend.rb +88 -0
  232. data/lib/rosett_ai/compiler/backends/generic_backend.rb +15 -0
  233. data/lib/rosett_ai/compiler/behaviour_compiler.rb +40 -0
  234. data/lib/rosett_ai/compiler/capability_checker.rb +104 -0
  235. data/lib/rosett_ai/compiler/compilation_pipeline.rb +361 -0
  236. data/lib/rosett_ai/compiler/compiled_output.rb +39 -0
  237. data/lib/rosett_ai/compiler/locale_compiler.rb +250 -0
  238. data/lib/rosett_ai/compiler/target_profile.rb +112 -0
  239. data/lib/rosett_ai/completion/generator.rb +101 -0
  240. data/lib/rosett_ai/completion/shells/bash_generator.rb +126 -0
  241. data/lib/rosett_ai/completion/shells/fish_generator.rb +78 -0
  242. data/lib/rosett_ai/completion/shells/zsh_generator.rb +126 -0
  243. data/lib/rosett_ai/comply/checkers/cra_checker.rb +102 -0
  244. data/lib/rosett_ai/comply/checkers/license_checker.rb +85 -0
  245. data/lib/rosett_ai/comply/checkers/spdx_header_checker.rb +98 -0
  246. data/lib/rosett_ai/comply/reporter.rb +113 -0
  247. data/lib/rosett_ai/comply/runner.rb +50 -0
  248. data/lib/rosett_ai/composition/circular_dependency_detector.rb +56 -0
  249. data/lib/rosett_ai/composition/composer.rb +158 -0
  250. data/lib/rosett_ai/composition/composition_result.rb +64 -0
  251. data/lib/rosett_ai/composition/conflict_detector.rb +53 -0
  252. data/lib/rosett_ai/composition/lockfile.rb +103 -0
  253. data/lib/rosett_ai/composition/merge_strategy.rb +131 -0
  254. data/lib/rosett_ai/composition/priority_sorter.rb +29 -0
  255. data/lib/rosett_ai/composition/scope_resolver.rb +55 -0
  256. data/lib/rosett_ai/config/compile_result.rb +37 -0
  257. data/lib/rosett_ai/config/compiler.rb +13 -0
  258. data/lib/rosett_ai/config/domain_transformer.rb +13 -0
  259. data/lib/rosett_ai/config/key_map.rb +13 -0
  260. data/lib/rosett_ai/config/masking_secret_resolver.rb +40 -0
  261. data/lib/rosett_ai/config/scope_router.rb +13 -0
  262. data/lib/rosett_ai/config/secret_resolver.rb +125 -0
  263. data/lib/rosett_ai/configuration.rb +119 -0
  264. data/lib/rosett_ai/content/content_client.rb +60 -0
  265. data/lib/rosett_ai/content/pack_installer.rb +117 -0
  266. data/lib/rosett_ai/content/pack_manifest.rb +50 -0
  267. data/lib/rosett_ai/content/pack_registry.rb +68 -0
  268. data/lib/rosett_ai/content_packs/manager.rb +50 -0
  269. data/lib/rosett_ai/dbus/compositor_detector.rb +77 -0
  270. data/lib/rosett_ai/dbus/focus_adapters/base.rb +59 -0
  271. data/lib/rosett_ai/dbus/focus_adapters/gnome_adapter.rb +172 -0
  272. data/lib/rosett_ai/dbus/focus_adapters/hyprland_adapter.rb +77 -0
  273. data/lib/rosett_ai/dbus/focus_adapters/i3_adapter.rb +65 -0
  274. data/lib/rosett_ai/dbus/focus_adapters/kwin_adapter.rb +103 -0
  275. data/lib/rosett_ai/dbus/focus_adapters/x11_adapter.rb +105 -0
  276. data/lib/rosett_ai/dbus/focus_monitor_interface.rb +103 -0
  277. data/lib/rosett_ai/dbus/manager_interface.rb +213 -0
  278. data/lib/rosett_ai/dbus/plugin_manager_interface.rb +169 -0
  279. data/lib/rosett_ai/dbus/rate_limiter.rb +89 -0
  280. data/lib/rosett_ai/dbus/service.rb +121 -0
  281. data/lib/rosett_ai/dbus/status_notifier_interface.rb +79 -0
  282. data/lib/rosett_ai/deprecation.rb +79 -0
  283. data/lib/rosett_ai/desktop/dbus_client.rb +259 -0
  284. data/lib/rosett_ai/desktop/gtk4_app.rb +371 -0
  285. data/lib/rosett_ai/desktop/gtk4_preferences.rb +331 -0
  286. data/lib/rosett_ai/desktop/gui_logger.rb +236 -0
  287. data/lib/rosett_ai/doctor/check.rb +92 -0
  288. data/lib/rosett_ai/doctor/checks/cache_health_check.rb +50 -0
  289. data/lib/rosett_ai/doctor/checks/dbus_availability_check.rb +39 -0
  290. data/lib/rosett_ai/doctor/checks/engine_detection_check.rb +46 -0
  291. data/lib/rosett_ai/doctor/checks/file_permission_check.rb +44 -0
  292. data/lib/rosett_ai/doctor/checks/gem_dependency_check.rb +55 -0
  293. data/lib/rosett_ai/doctor/checks/ruby_version_check.rb +50 -0
  294. data/lib/rosett_ai/doctor/checks/stale_config_nncc_check.rb +57 -0
  295. data/lib/rosett_ai/doctor/checks/stale_home_nncc_check.rb +59 -0
  296. data/lib/rosett_ai/doctor.rb +81 -0
  297. data/lib/rosett_ai/documentation/reference_compiler.rb +122 -0
  298. data/lib/rosett_ai/documentation/translator.rb +62 -0
  299. data/lib/rosett_ai/engines/base_config_compiler.rb +203 -0
  300. data/lib/rosett_ai/engines/detector.rb +63 -0
  301. data/lib/rosett_ai/engines/registry.rb +50 -0
  302. data/lib/rosett_ai/error_handler.rb +139 -0
  303. data/lib/rosett_ai/exit_codes.rb +76 -0
  304. data/lib/rosett_ai/feature_flags.rb +102 -0
  305. data/lib/rosett_ai/formatting.rb +33 -0
  306. data/lib/rosett_ai/gem_consistency_checker.rb +199 -0
  307. data/lib/rosett_ai/git_hooks/chain_detector.rb +86 -0
  308. data/lib/rosett_ai/git_hooks/installer.rb +175 -0
  309. data/lib/rosett_ai/git_hooks/script_generator.rb +125 -0
  310. data/lib/rosett_ai/gitlab/validators/supplementary_gitlab_ci_yaml_validator.rb +79 -0
  311. data/lib/rosett_ai/i18n/locale_resolver.rb +46 -0
  312. data/lib/rosett_ai/i18n/utf8_checker.rb +32 -0
  313. data/lib/rosett_ai/init/config_file_writer.rb +24 -0
  314. data/lib/rosett_ai/init/directory_builder.rb +38 -0
  315. data/lib/rosett_ai/init/file_copier.rb +95 -0
  316. data/lib/rosett_ai/init/global_initializer.rb +28 -0
  317. data/lib/rosett_ai/init/local_initializer.rb +27 -0
  318. data/lib/rosett_ai/init/mcp_registrar.rb +109 -0
  319. data/lib/rosett_ai/init/project_initializer.rb +38 -0
  320. data/lib/rosett_ai/licensing/license_key.rb +139 -0
  321. data/lib/rosett_ai/licensing/license_store.rb +64 -0
  322. data/lib/rosett_ai/licensing/license_validator.rb +60 -0
  323. data/lib/rosett_ai/licensing/tier.rb +42 -0
  324. data/lib/rosett_ai/mcp/admin/auditor.rb +88 -0
  325. data/lib/rosett_ai/mcp/admin/health_checker.rb +81 -0
  326. data/lib/rosett_ai/mcp/admin/registry.rb +100 -0
  327. data/lib/rosett_ai/mcp/admin/schema_validator.rb +63 -0
  328. data/lib/rosett_ai/mcp/enforcement/.gitkeep +0 -0
  329. data/lib/rosett_ai/mcp/enforcement/hook_generator.rb +197 -0
  330. data/lib/rosett_ai/mcp/enforcement/validator.rb +215 -0
  331. data/lib/rosett_ai/mcp/governance.rb +160 -0
  332. data/lib/rosett_ai/mcp/http_security_config.rb +158 -0
  333. data/lib/rosett_ai/mcp/instructions.rb +266 -0
  334. data/lib/rosett_ai/mcp/key_hasher.rb +66 -0
  335. data/lib/rosett_ai/mcp/keyfile.rb +221 -0
  336. data/lib/rosett_ai/mcp/middleware/authentication.rb +146 -0
  337. data/lib/rosett_ai/mcp/middleware/content_type.rb +56 -0
  338. data/lib/rosett_ai/mcp/middleware/cors.rb +83 -0
  339. data/lib/rosett_ai/mcp/middleware/origin_validation.rb +73 -0
  340. data/lib/rosett_ai/mcp/middleware/rate_limit.rb +106 -0
  341. data/lib/rosett_ai/mcp/middleware/request_size.rb +51 -0
  342. data/lib/rosett_ai/mcp/plugins.rb +143 -0
  343. data/lib/rosett_ai/mcp/prompts/compilation_prompt.rb +40 -0
  344. data/lib/rosett_ai/mcp/prompts/compliance_prompt.rb +41 -0
  345. data/lib/rosett_ai/mcp/prompts/diagnostics_prompt.rb +41 -0
  346. data/lib/rosett_ai/mcp/prompts/validation_prompt.rb +41 -0
  347. data/lib/rosett_ai/mcp/resources/behaviour_resource.rb +127 -0
  348. data/lib/rosett_ai/mcp/resources/config_resource.rb +72 -0
  349. data/lib/rosett_ai/mcp/resources/design_resource.rb +58 -0
  350. data/lib/rosett_ai/mcp/resources/hooks_resource.rb +74 -0
  351. data/lib/rosett_ai/mcp/resources/provenance_resource.rb +51 -0
  352. data/lib/rosett_ai/mcp/resources/rules_resource.rb +60 -0
  353. data/lib/rosett_ai/mcp/resources/schema_resource.rb +72 -0
  354. data/lib/rosett_ai/mcp/response_helper.rb +46 -0
  355. data/lib/rosett_ai/mcp/security_logger.rb +60 -0
  356. data/lib/rosett_ai/mcp/server.rb +212 -0
  357. data/lib/rosett_ai/mcp/settings/server_installer.rb +112 -0
  358. data/lib/rosett_ai/mcp/settings/trust_manager.rb +142 -0
  359. data/lib/rosett_ai/mcp/tools/adopt_tool.rb +70 -0
  360. data/lib/rosett_ai/mcp/tools/backup_tool.rb +64 -0
  361. data/lib/rosett_ai/mcp/tools/behaviour_display_tool.rb +72 -0
  362. data/lib/rosett_ai/mcp/tools/behaviour_list_tool.rb +56 -0
  363. data/lib/rosett_ai/mcp/tools/behaviour_manage_tool.rb +114 -0
  364. data/lib/rosett_ai/mcp/tools/behaviour_show_tool.rb +62 -0
  365. data/lib/rosett_ai/mcp/tools/compile_status_tool.rb +122 -0
  366. data/lib/rosett_ai/mcp/tools/compile_tool.rb +191 -0
  367. data/lib/rosett_ai/mcp/tools/comply_tool.rb +79 -0
  368. data/lib/rosett_ai/mcp/tools/config_compile_tool.rb +71 -0
  369. data/lib/rosett_ai/mcp/tools/config_status_tool.rb +79 -0
  370. data/lib/rosett_ai/mcp/tools/content_tool.rb +78 -0
  371. data/lib/rosett_ai/mcp/tools/context_query_tool.rb +156 -0
  372. data/lib/rosett_ai/mcp/tools/design_list_tool.rb +57 -0
  373. data/lib/rosett_ai/mcp/tools/design_show_tool.rb +69 -0
  374. data/lib/rosett_ai/mcp/tools/doctor_tool.rb +62 -0
  375. data/lib/rosett_ai/mcp/tools/documentation_status_tool.rb +45 -0
  376. data/lib/rosett_ai/mcp/tools/engines_tool.rb +84 -0
  377. data/lib/rosett_ai/mcp/tools/hook_install_tool.rb +190 -0
  378. data/lib/rosett_ai/mcp/tools/hook_preview_tool.rb +173 -0
  379. data/lib/rosett_ai/mcp/tools/hooks_status_tool.rb +84 -0
  380. data/lib/rosett_ai/mcp/tools/init_tool.rb +87 -0
  381. data/lib/rosett_ai/mcp/tools/license_status_tool.rb +44 -0
  382. data/lib/rosett_ai/mcp/tools/project_tool.rb +117 -0
  383. data/lib/rosett_ai/mcp/tools/provenance_tool.rb +97 -0
  384. data/lib/rosett_ai/mcp/tools/provenance_write_tool.rb +40 -0
  385. data/lib/rosett_ai/mcp/tools/retrofit_tool.rb +81 -0
  386. data/lib/rosett_ai/mcp/tools/rule_search_tool.rb +163 -0
  387. data/lib/rosett_ai/mcp/tools/schema_get_tool.rb +94 -0
  388. data/lib/rosett_ai/mcp/tools/tooling_tool.rb +86 -0
  389. data/lib/rosett_ai/mcp/tools/validate_tool.rb +105 -0
  390. data/lib/rosett_ai/mcp/tools/workflow_execute_tool.rb +74 -0
  391. data/lib/rosett_ai/mcp/tools/workflow_tool.rb +78 -0
  392. data/lib/rosett_ai/migration/detector.rb +117 -0
  393. data/lib/rosett_ai/migration/nncc_config_migrator.rb +94 -0
  394. data/lib/rosett_ai/migration/nncc_project_migrator.rb +90 -0
  395. data/lib/rosett_ai/migration/xdg_migrator.rb +123 -0
  396. data/lib/rosett_ai/package_manager/apt.rb +108 -0
  397. data/lib/rosett_ai/package_manager/base.rb +68 -0
  398. data/lib/rosett_ai/package_manager/gem_backend.rb +90 -0
  399. data/lib/rosett_ai/packaging/variant_config.rb +92 -0
  400. data/lib/rosett_ai/path_resolver.rb +115 -0
  401. data/lib/rosett_ai/plugins/contract.rb +43 -0
  402. data/lib/rosett_ai/plugins/engine_contract.rb +60 -0
  403. data/lib/rosett_ai/plugins/gui_contract.rb +74 -0
  404. data/lib/rosett_ai/plugins/mcp_contract.rb +48 -0
  405. data/lib/rosett_ai/plugins/registry.rb +150 -0
  406. data/lib/rosett_ai/policy/auditor.rb +41 -0
  407. data/lib/rosett_ai/policy/deny_list.rb +71 -0
  408. data/lib/rosett_ai/policy/opt_out_scanner.rb +37 -0
  409. data/lib/rosett_ai/policy/policy_compiler.rb +84 -0
  410. data/lib/rosett_ai/policy/protected_files.rb +47 -0
  411. data/lib/rosett_ai/policy/tier_hierarchy.rb +48 -0
  412. data/lib/rosett_ai/policy/validator.rb +35 -0
  413. data/lib/rosett_ai/profiler.rb +79 -0
  414. data/lib/rosett_ai/project/drift_detector.rb +126 -0
  415. data/lib/rosett_ai/project/manager.rb +115 -0
  416. data/lib/rosett_ai/project/sync_manager.rb +138 -0
  417. data/lib/rosett_ai/project/template_applier.rb +105 -0
  418. data/lib/rosett_ai/project_context.rb +82 -0
  419. data/lib/rosett_ai/provenance/entry.rb +63 -0
  420. data/lib/rosett_ai/provenance/file_source.rb +32 -0
  421. data/lib/rosett_ai/provenance/source.rb +62 -0
  422. data/lib/rosett_ai/provenance/store.rb +153 -0
  423. data/lib/rosett_ai/provenance/tracker.rb +62 -0
  424. data/lib/rosett_ai/provenance/trailer_generator.rb +43 -0
  425. data/lib/rosett_ai/provenance/validator.rb +45 -0
  426. data/lib/rosett_ai/quorum/collector.rb +59 -0
  427. data/lib/rosett_ai/quorum/comparator.rb +81 -0
  428. data/lib/rosett_ai/quorum/dispatcher.rb +57 -0
  429. data/lib/rosett_ai/quorum/strategies/adopt.rb +56 -0
  430. data/lib/rosett_ai/rai_config.rb +107 -0
  431. data/lib/rosett_ai/retrofit/base_parser.rb +66 -0
  432. data/lib/rosett_ai/retrofit/engine.rb +171 -0
  433. data/lib/rosett_ai/retrofit/parsers/agents_md_parser.rb +50 -0
  434. data/lib/rosett_ai/retrofit/parsers/claude_parser.rb +69 -0
  435. data/lib/rosett_ai/retrofit/parsers/cursor_parser.rb +82 -0
  436. data/lib/rosett_ai/retrofit/round_trip_validator.rb +65 -0
  437. data/lib/rosett_ai/retrofit/scanner.rb +47 -0
  438. data/lib/rosett_ai/retrofit/secret_detector.rb +87 -0
  439. data/lib/rosett_ai/secrets_resolver.rb +71 -0
  440. data/lib/rosett_ai/smart_feedback/suggester.rb +83 -0
  441. data/lib/rosett_ai/smart_feedback/thor_middleware.rb +84 -0
  442. data/lib/rosett_ai/structured_logger.rb +110 -0
  443. data/lib/rosett_ai/telemetry/json_lines_writer.rb +50 -0
  444. data/lib/rosett_ai/telemetry/log_rotator.rb +67 -0
  445. data/lib/rosett_ai/telemetry/provider.rb +26 -0
  446. data/lib/rosett_ai/telemetry/reporter.rb +144 -0
  447. data/lib/rosett_ai/telemetry.rb +47 -0
  448. data/lib/rosett_ai/text_sanitizer.rb +62 -0
  449. data/lib/rosett_ai/thor/cli.rb +269 -0
  450. data/lib/rosett_ai/thor/tasks/adopt.rb +250 -0
  451. data/lib/rosett_ai/thor/tasks/backup.rb +420 -0
  452. data/lib/rosett_ai/thor/tasks/behaviour.rb +474 -0
  453. data/lib/rosett_ai/thor/tasks/build.rb +1162 -0
  454. data/lib/rosett_ai/thor/tasks/compile.rb +415 -0
  455. data/lib/rosett_ai/thor/tasks/completion.rb +123 -0
  456. data/lib/rosett_ai/thor/tasks/comply.rb +82 -0
  457. data/lib/rosett_ai/thor/tasks/config.rb +265 -0
  458. data/lib/rosett_ai/thor/tasks/content.rb +193 -0
  459. data/lib/rosett_ai/thor/tasks/dbus.rb +321 -0
  460. data/lib/rosett_ai/thor/tasks/design.rb +258 -0
  461. data/lib/rosett_ai/thor/tasks/desktop.rb +129 -0
  462. data/lib/rosett_ai/thor/tasks/doctor.rb +127 -0
  463. data/lib/rosett_ai/thor/tasks/documentation.rb +321 -0
  464. data/lib/rosett_ai/thor/tasks/engines.rb +167 -0
  465. data/lib/rosett_ai/thor/tasks/hooks.rb +219 -0
  466. data/lib/rosett_ai/thor/tasks/init.rb +259 -0
  467. data/lib/rosett_ai/thor/tasks/license.rb +120 -0
  468. data/lib/rosett_ai/thor/tasks/mcp.rb +535 -0
  469. data/lib/rosett_ai/thor/tasks/migrate.rb +121 -0
  470. data/lib/rosett_ai/thor/tasks/plugins.rb +157 -0
  471. data/lib/rosett_ai/thor/tasks/project.rb +260 -0
  472. data/lib/rosett_ai/thor/tasks/provenance.rb +195 -0
  473. data/lib/rosett_ai/thor/tasks/release.rb +314 -0
  474. data/lib/rosett_ai/thor/tasks/retrofit.rb +90 -0
  475. data/lib/rosett_ai/thor/tasks/tooling.rb +308 -0
  476. data/lib/rosett_ai/thor/tasks/validate.rb +108 -0
  477. data/lib/rosett_ai/thor/tasks/workflow.rb +196 -0
  478. data/lib/rosett_ai/tooling/ci_yaml_validator.rb +37 -0
  479. data/lib/rosett_ai/tooling/version_checker.rb +35 -0
  480. data/lib/rosett_ai/ui/accessible_tui.rb +61 -0
  481. data/lib/rosett_ai/ui/base.rb +46 -0
  482. data/lib/rosett_ai/ui/gtk4.rb +98 -0
  483. data/lib/rosett_ai/ui/kde.rb +40 -0
  484. data/lib/rosett_ai/ui/qt6.rb +40 -0
  485. data/lib/rosett_ai/ui/registry.rb +60 -0
  486. data/lib/rosett_ai/ui/tty_helper.rb +74 -0
  487. data/lib/rosett_ai/ui/tui.rb +59 -0
  488. data/lib/rosett_ai/validators/behaviour_validator.rb +20 -0
  489. data/lib/rosett_ai/validators/design_validator.rb +17 -0
  490. data/lib/rosett_ai/validators/schema_validator.rb +84 -0
  491. data/lib/rosett_ai/validators/tooling_validator.rb +17 -0
  492. data/lib/rosett_ai/version.rb +8 -0
  493. data/lib/rosett_ai/version_consistency_checker.rb +129 -0
  494. data/lib/rosett_ai/workflow/audit_log.rb +86 -0
  495. data/lib/rosett_ai/workflow/engine.rb +142 -0
  496. data/lib/rosett_ai/workflow/manager.rb +82 -0
  497. data/lib/rosett_ai/workflow/schema_validator.rb +71 -0
  498. data/lib/rosett_ai/workflow/step_runner.rb +61 -0
  499. data/lib/rosett_ai/workflow/steps/prompt_step.rb +62 -0
  500. data/lib/rosett_ai/workflow/steps/rai_step.rb +74 -0
  501. data/lib/rosett_ai/workflow/steps/shell_step.rb +53 -0
  502. data/lib/rosett_ai/yaml_loader.rb +78 -0
  503. data/lib/rosett_ai.rb +221 -0
  504. data/lib/rubocop/cop/rosett_ai/shell_interpolation.rb +54 -0
  505. data/lib/rubocop/cop/rosett_ai/unsafe_const_get.rb +60 -0
  506. data/lib/rubocop/cop/rosett_ai/unsafe_send.rb +50 -0
  507. data/lib/rubocop/cop/rosett_ai/unsafe_yaml_load.rb +40 -0
  508. data/lib/rubocop/rosett_ai.rb +9 -0
  509. data/lib/scripts/generated/docker_hub_tags.rb +126 -0
  510. data/locales/.gitkeep +0 -0
  511. data/locales/ar.yml +579 -0
  512. data/locales/en.yml +571 -0
  513. data/locales/fr.yml +567 -0
  514. data/packaging/build-engine-deb.sh +81 -0
  515. data/packaging/scripts/postinst +17 -0
  516. data/packaging/scripts/postrm +19 -0
  517. data/packaging/scripts/prerm +10 -0
  518. data/packaging/wrapper.sh.template +38 -0
  519. data/rosett-ai.gemspec +63 -0
  520. data/rules/.gitkeep +0 -0
  521. data/scripts/publish/pulp_upload.sh +123 -0
  522. data/settings.json +29 -0
  523. data/share/applications/be.neatnerds.rosettai.desktop +29 -0
  524. data/share/dbus-1/interfaces/be.neatnerds.rosettai.xml +103 -0
  525. data/share/dbus-1/services/be.neatnerds.rosettai.service +3 -0
  526. data/share/templates/behaviour/criticalthinking.yml +69 -0
  527. metadata +810 -0
@@ -0,0 +1,158 @@
1
+ # ADR-002: Path Resolution Strategy
2
+
3
+ ## Status
4
+
5
+ Accepted (2026-02-21)
6
+
7
+ ## Context
8
+
9
+ raictl manages two distinct path domains:
10
+
11
+ 1. **Claude Code's directory** (`~/.claude/`) — rules, settings, CLAUDE.md.
12
+ This path is Anthropic's convention, established by Claude Code itself.
13
+ We do not own or control this convention.
14
+
15
+ 2. **rosett-ai's own configuration** (`~/.config/rosett-ai/`) — secrets, license keys,
16
+ content packs. This path is already established in our design documents
17
+ (`security.yml`, `licensing_system.yml`, `content_packs.yml`) and
18
+ implemented in `SecretsResolver`.
19
+
20
+ Currently, 6 Thor task files contain hardcoded `~/.claude` paths:
21
+
22
+ | File | Hardcoded path |
23
+ |------|---------------|
24
+ | `lib/rosett_ai/thor/tasks/init.rb` | `File.expand_path('~/.claude')` |
25
+ | `lib/rosett_ai/thor/tasks/backup.rb` | `File.expand_path('~/.claude')` |
26
+ | `lib/rosett_ai/thor/tasks/compile.rb` | `File.expand_path('~/.claude/rules')` |
27
+ | `lib/rosett_ai/thor/tasks/adopt.rb` | `File.expand_path('~/.claude/rules')` |
28
+ | `lib/rosett_ai/compiler/behaviour_compiler.rb` | `~/.claude/rules/` (string literal) |
29
+ | `lib/rosett_ai/thor/tasks/backup.rb` | `File.join(Dir.pwd, '.claude')` (local) |
30
+
31
+ The `architecture.yml` design document lists "hardcoded paths instead of
32
+ XDG-compliant directory resolution" as an anti-pattern (line 64).
33
+
34
+ ### XDG Base Directory Specification
35
+
36
+ The [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir/latest/)
37
+ from freedesktop.org defines standard locations for user files:
38
+
39
+ | Variable | Default | Purpose |
40
+ |----------|---------|---------|
41
+ | `XDG_CONFIG_HOME` | `~/.config` | User-specific configuration |
42
+ | `XDG_DATA_HOME` | `~/.local/share` | User-specific data files |
43
+ | `XDG_CACHE_HOME` | `~/.cache` | Non-essential cached data |
44
+ | `XDG_STATE_HOME` | `~/.local/state` | User-specific state data |
45
+
46
+ Desktop environment compliance:
47
+
48
+ | DE | XDG compliant | Notes |
49
+ |----|--------------|-------|
50
+ | GNOME | Yes | freedesktop.org founding member |
51
+ | KDE Plasma | Yes | KDE UserBase documented |
52
+ | XFCE | Yes | Adopted spec |
53
+ | MATE | Yes | GNOME 2 fork, inherited |
54
+ | Cinnamon | Yes | GNOME fork, inherited |
55
+ | Headless/TTY | N/A | No DE; apps read env vars or use defaults |
56
+ | Tiling WMs | N/A | Apps decide individually |
57
+
58
+ Ruby ecosystem: RubyGems 3.0+ follows XDG. Pry follows XDG. Bundler supports
59
+ it with environment variables.
60
+
61
+ Sources:
62
+ [ArchWiki XDG Base Directory](https://wiki.archlinux.org/title/XDG_Base_Directory),
63
+ [freedesktop.org spec](https://specifications.freedesktop.org/basedir/latest/),
64
+ [KDE XDG Hierarchy](https://userbase.kde.org/KDE_System_Administration/XDG_Filesystem_Hierarchy).
65
+
66
+ ## Options
67
+
68
+ ### Option A: PathResolver (centralise, keep ~/.claude)
69
+
70
+ Create a single `RosettAi::PathResolver` module that owns all path constants.
71
+ Claude Code paths stay at `~/.claude` (respecting upstream convention). rosett-ai's
72
+ own config uses `XDG_CONFIG_HOME/rosett-ai` (defaults to `~/.config/rosett-ai`). All
73
+ hardcoded paths in Thor tasks are replaced with `PathResolver.rules_dir`,
74
+ `PathResolver.global_dir`, etc.
75
+
76
+ Pros:
77
+
78
+ - Single source of truth — one file to update if conventions change
79
+ - Testable: specs override PathResolver methods, no filesystem coupling
80
+ - DE-agnostic: works on any desktop environment or headless
81
+ - Consistent with existing design docs (SecretsResolver already uses
82
+ `~/.config/rosett-ai/`)
83
+ - Respects both upstream convention (Claude Code) and XDG (rosett-ai data)
84
+
85
+ Cons:
86
+
87
+ - Still "hardcodes" `~/.claude`, just centralised in one place
88
+ - New dependency for all path-using code
89
+
90
+ ### Option B: Full XDG everywhere
91
+
92
+ Move both Claude Code AND rai config under XDG directories. Claude Code rules
93
+ would move to something like `~/.local/share/rosett-ai/claude/rules`.
94
+
95
+ Pros:
96
+
97
+ - Maximum standards compliance with freedesktop.org
98
+
99
+ Cons:
100
+
101
+ - Breaking change: Claude Code expects `~/.claude/`
102
+ - Users must reconfigure or symlink existing `~/.claude`
103
+ - Fights upstream convention for no practical gain
104
+ - Claude Code itself does not follow XDG
105
+
106
+ ### Option C: Status quo + ADR (no code change)
107
+
108
+ Document why we keep `~/.claude` and stop there.
109
+
110
+ Pros:
111
+
112
+ - No code changes needed
113
+
114
+ Cons:
115
+
116
+ - Hardcoded paths remain scattered across 6+ files
117
+ - Every new feature adds more hardcoded paths
118
+ - Testing remains coupled to filesystem (cannot mock paths)
119
+ - Future refactor scope grows with each commit
120
+ - Path changes require grep-and-replace with risk of partial updates
121
+ - architecture.yml anti-pattern stays unresolved
122
+
123
+ ## Decision
124
+
125
+ **Option A: PathResolver** — centralise all paths in `RosettAi::PathResolver` with
126
+ dual-domain separation.
127
+
128
+ Rationale:
129
+
130
+ - We do not own `~/.claude` — Anthropic does. Changing it would break the
131
+ Claude Code integration that rosett-ai exists to manage.
132
+ - We already use `~/.config/rosett-ai` for our own data (secrets, future license
133
+ keys, content packs). This is XDG-compliant.
134
+ - Centralising eliminates the scattered-hardcoding anti-pattern without
135
+ fighting upstream conventions.
136
+ - Testability: specs can override `PathResolver.global_dir` or inject a
137
+ mock, removing filesystem coupling from unit tests.
138
+ - If Claude Code ever changes its directory convention, one file changes
139
+ instead of 6+.
140
+
141
+ ## Consequences
142
+
143
+ - New file: `lib/rosett_ai/path_resolver.rb` with instance methods (future-proofed
144
+ for ADR-006 multi-engine via `engine:` keyword)
145
+ - All 6 Thor task files + compiler updated to use `RosettAi.paths.*`
146
+ - `~/.config/rosett-ai/` created by `bin/raictl init --global` alongside `~/.claude/`
147
+ - `XDG_CONFIG_HOME` respected for rosett-ai data (non-default setups work)
148
+ - `SecretsResolver` updated to use `RosettAi.paths.rai_config_dir` instead of
149
+ its own hardcoded `File.join(Dir.home, '.config', 'rosett-ai')`
150
+ - `SECRETS_DIR` and `SECRETS_FILE` constants removed from SecretsResolver
151
+ - Lockfile output uses `rules_display_path` (tilde form) for portability
152
+ - Specs use `instance_double(RosettAi::PathResolver)` instead of `stub_const` or
153
+ `allow(File).to receive(:expand_path)` — cleaner, no filesystem coupling
154
+ - Future: if Claude Code changes conventions, one file to update
155
+
156
+ ## Implementation
157
+
158
+ Implemented in Phase 3 (2026-02-21). See `doc/changes/2026-02-21-path-resolver.md`.
@@ -0,0 +1,193 @@
1
+ # ADR-003: UI Adapter Selection Mechanism
2
+
3
+ ## Status
4
+
5
+ Accepted (2026-02-21) -- implementation deferred to P3
6
+
7
+ ## Context
8
+
9
+ The `RosettAi::Ui::Registry` pattern is implemented. UI adapters register
10
+ themselves at require time (`Registry.register(:tui, Tui)`), and
11
+ `Registry.resolve(name)` instantiates the requested adapter. Currently only
12
+ the TUI adapter exists and it is selected automatically as the default.
13
+
14
+ When GUI packages (`rosett-ai-gtk4`, `rosett-ai-qt6`) are introduced (P3), users will
15
+ need a mechanism to select which adapter to use. The `architecture.yml` design
16
+ document gives the example `bin/raictl --ui gtk4`, and the `ui_framework.yml`
17
+ document states TUI is the default with GUI packages as separate Debian
18
+ packages.
19
+
20
+ The selection mechanism must be decided now (P2) so the architecture is ready
21
+ for P3 implementation.
22
+
23
+ ### Existing precedent
24
+
25
+ raictl already uses an ordered resolution pattern in `SecretsResolver`:
26
+ ENV variable > secrets file > raise error. This pattern is understood and
27
+ tested.
28
+
29
+ ### Multi-engine constraint
30
+
31
+ ADR-006 establishes that rosett-ai should be engine-agnostic. The UI adapter
32
+ selection must therefore not depend on any engine-specific configuration
33
+ file (e.g. Claude Code's `settings.json`). The UI adapter is an rosett-ai
34
+ concern and must be resolved from rosett-ai's own configuration.
35
+
36
+ ## Options
37
+
38
+ ### Option A: Global CLI flag only
39
+
40
+ Add `--ui NAME` as a `class_option` on `Thor::CLI` (inherited by all
41
+ subcommands). Selection happens via `Registry.resolve(options[:ui])`.
42
+
43
+ Pros:
44
+
45
+ - Explicit, visible in help output
46
+ - No configuration infrastructure needed
47
+
48
+ Cons:
49
+
50
+ - Must be passed every invocation
51
+ - No way to set a persistent default
52
+
53
+ ### Option B: Config file setting only
54
+
55
+ Add `preferred_ui: gtk4` to rosett-ai's own configuration. Read at startup.
56
+
57
+ Pros:
58
+
59
+ - Set once, works always
60
+ - Aligns with rosett-ai's own config infrastructure
61
+
62
+ Cons:
63
+
64
+ - No quick override for one-off invocations
65
+
66
+ ### Option C: Environment variable only
67
+
68
+ Read `RAI_UI` environment variable. Default to `tui` if unset.
69
+
70
+ Pros:
71
+
72
+ - Simple, shell-profile level persistence
73
+ - No code infrastructure needed
74
+
75
+ Cons:
76
+
77
+ - Less discoverable than CLI flag
78
+ - Not visible in `--help` output
79
+
80
+ ### Option D: Cascade precedence (revised)
81
+
82
+ Resolution order:
83
+
84
+ ```text
85
+ 1. --ui NAME CLI flag override (highest priority)
86
+ 2. RAI_UI Environment variable (session-level)
87
+ 3. rai config Ordered adapter list (first available wins)
88
+ 4. DE auto-detection Detect runtime desktop environment
89
+ 5. :tui Always-available fallback (lowest priority)
90
+ ```
91
+
92
+ Each layer is consulted in order. The first layer that produces a result
93
+ wins. Layers 3-5 check adapter availability via `Registry.available?`
94
+ before selecting — an adapter whose package is not installed is skipped.
95
+
96
+ Layer 3 -- rai config adapter list:
97
+
98
+ The rai configuration file (`~/.config/rosett-ai/`) contains an ordered
99
+ array of preferred adapters. The first adapter in the list whose package
100
+ is installed wins:
101
+
102
+ ```yaml
103
+ ui:
104
+ adapters:
105
+ - gtk4
106
+ - qt6
107
+ - tui
108
+ ```
109
+
110
+ Array position is priority — no explicit priority numbers needed. This
111
+ is simpler and YAML arrays are inherently ordered. If per-project
112
+ overrides with cross-source merging are needed in the future, explicit
113
+ priority numbers can be added without breaking the simple form.
114
+
115
+ Layer 4 -- Desktop environment auto-detection:
116
+
117
+ When no explicit preference is configured, rosett-ai detects the running
118
+ desktop environment at runtime via XDG environment variables and selects
119
+ a matching adapter if its package is installed:
120
+
121
+ | Env var | Value | Adapter |
122
+ |---------|-------|---------|
123
+ | `XDG_CURRENT_DESKTOP` | `GNOME` | gtk4 |
124
+ | `XDG_CURRENT_DESKTOP` | `KDE` | qt6 |
125
+ | `XDG_CURRENT_DESKTOP` | `XFCE` | gtk4 (GTK-based) |
126
+ | `XDG_CURRENT_DESKTOP` | `MATE` | gtk4 (GTK-based) |
127
+ | `XDG_CURRENT_DESKTOP` | `Cinnamon` | gtk4 (GTK-based) |
128
+ | `XDG_SESSION_TYPE` | `tty` | tui |
129
+ | Unset / empty | -- | tui |
130
+
131
+ Note: `XDG_CURRENT_DESKTOP` can be colon-separated (e.g. `ubuntu:GNOME`).
132
+ The detection logic must handle this by checking each component.
133
+
134
+ Auto-detection only suggests adapters whose packages are installed. If
135
+ `XDG_CURRENT_DESKTOP=GNOME` but `rosett-ai-gtk4` is not installed, detection
136
+ falls through to TUI.
137
+
138
+ Pros:
139
+
140
+ - Most flexible — covers all use cases
141
+ - Follows principle of least surprise
142
+ - Consistent with existing `SecretsResolver` precedence pattern
143
+ - Users can set-and-forget via env or config
144
+ - Power users can override per-invocation
145
+ - Zero-config experience: install rosett-ai + rosett-ai-gtk4 on GNOME, it just works
146
+ - Users can switch desktop environments without reconfiguring rosett-ai
147
+ - Engine-agnostic: no dependency on any AI engine's config format
148
+
149
+ Cons:
150
+
151
+ - 5 levels to test
152
+ - Debug confusion if user forgets they set an env var or config
153
+
154
+ The debug confusion is mitigated by the `rai config show` command (see
155
+ Consequences below).
156
+
157
+ ## Decision
158
+
159
+ **Option D: Cascade precedence (revised)** — because:
160
+
161
+ - Consistent with the existing resolution pattern in `SecretsResolver`
162
+ (ENV > file > raise). Adding a CLI flag as highest priority and
163
+ auto-detection as a smart fallback is a natural extension.
164
+ - Every use case is covered: one-off override (flag), session default
165
+ (env), persistent preference (config), smart default (DE detection),
166
+ and guaranteed fallback (TUI).
167
+ - Engine-agnostic: the cascade uses rosett-ai's own configuration, not any
168
+ engine-specific settings file. This aligns with ADR-006.
169
+ - When `rosett-ai-gtk4` is installed on a GNOME desktop, it works out of the
170
+ box with zero configuration via auto-detection. The user can still
171
+ override with `--ui tui` for a headless session.
172
+ - The cascade is simple to explain: "flag wins, then env, then config
173
+ list, then desktop detection, then TUI".
174
+
175
+ ## Consequences
176
+
177
+ Implementation is deferred to P3 (ui_framework), but the architecture is
178
+ documented now:
179
+
180
+ - New `class_option :ui` on `Thor::CLI` (type: string, default: nil)
181
+ - Resolution logic in a `resolve_ui_adapter` method that implements the
182
+ 5-level cascade
183
+ - `Registry.resolve` called once at CLI startup with the resolved name
184
+ - `Registry.available?(name)` used at layers 3-5 to skip missing adapters
185
+ - Adapter instance available to tasks that produce output
186
+ - `RAI_UI` documented in `bin/raictl --help` and `doc/USAGE.md`
187
+ - New CLI commands for configuration transparency (P3):
188
+ - `bin/raictl config show` — display full rai configuration
189
+ - `bin/raictl config show ui` — display UI configuration with active
190
+ adapter and resolution source (e.g. "gtk4 (from: DE auto-detection)")
191
+ - Desktop environment detection via `XDG_CURRENT_DESKTOP` and
192
+ `XDG_SESSION_TYPE`, with colon-separated value handling
193
+ - P2 code does not need the flag yet — TUI is the only adapter
@@ -0,0 +1,179 @@
1
+ # ADR-004: Design Document Validation
2
+
3
+ ## Status
4
+
5
+ Accepted (2026-02-21)
6
+
7
+ ## Context
8
+
9
+ The `architecture.yml` design document (criterion 6) requires that
10
+ `conf/design/` documents are validated against `conf/schemas/design_schema.json`.
11
+
12
+ The schema already exists (206 lines, comprehensive) and covers all required
13
+ fields: name, domain, version, status, intent, constraints, acceptance_criteria,
14
+ plus optional sections for examples, anti_patterns, preferences, assets,
15
+ tui_layout, gui_notes, interactions, and accessibility.
16
+
17
+ There are currently 13 design documents in `conf/design/`. No validation
18
+ infrastructure exists for them.
19
+
20
+ ### Existing pattern
21
+
22
+ `RosettAi::Validators::BehaviourValidator` validates behaviour configuration files
23
+ against `conf/schemas/behaviour_schema.json`. It uses JSONSchemer, provides a
24
+ `valid?`/`validate`/`errors` API, and is wired into the CLI via
25
+ `bin/raictl behaviour validate [NAME]`. This is a proven, tested pattern.
26
+
27
+ ### Current schema inventory
28
+
29
+ Three schemas exist in `conf/schemas/`, but only one has a validator:
30
+
31
+ | Schema file | Validator class | CLI command | CI job |
32
+ |-------------|----------------|-------------|--------|
33
+ | `behaviour_schema.json` | `BehaviourValidator` | `behaviour validate` | Yes |
34
+ | `design_schema.json` | *(none)* | *(none)* | *(none)* |
35
+ | `tooling_schema.json` | *(none)* | *(none)* | *(none)* |
36
+
37
+ This ADR addresses all three — not just design documents.
38
+
39
+ ### Code duplication concern
40
+
41
+ `BehaviourValidator` is 80 lines. Creating a `DesignValidator` and a
42
+ `ToolingValidator` with the same logic but different schema paths would be
43
+ near-duplicates. With further validators coming in P2 (compiler, CC
44
+ configuration), duplication becomes a maintenance burden.
45
+
46
+ ## Options
47
+
48
+ ### Option A: Dedicated validator class per schema
49
+
50
+ Mirror `BehaviourValidator` exactly for each schema — same interface,
51
+ different schema path.
52
+
53
+ ```ruby
54
+ class DesignValidator
55
+ SCHEMA_PATH = 'conf/schemas/design_schema.json'
56
+ # ... identical logic to BehaviourValidator
57
+ end
58
+ ```
59
+
60
+ Pros:
61
+
62
+ - Consistent pattern, easy to understand
63
+ - No refactoring of existing code
64
+
65
+ Cons:
66
+
67
+ - Near-duplicate of `BehaviourValidator` (same logic, different constant)
68
+ - Each future validator adds another copy
69
+ - Bug fixes must be applied to all copies
70
+
71
+ ### Option B: Generic SchemaValidator with schema parameter
72
+
73
+ Extract the shared logic into `RosettAi::Validators::SchemaValidator`. Accept
74
+ the schema filename in the constructor. All validators become thin wrappers.
75
+
76
+ ```ruby
77
+ class SchemaValidator
78
+ def initialize(schema:)
79
+ @schema_path = RosettAi.root.join('conf', 'schemas', schema)
80
+ # ... shared logic
81
+ end
82
+ end
83
+
84
+ # Thin wrappers
85
+ class BehaviourValidator < SchemaValidator
86
+ def initialize = super(schema: 'behaviour_schema.json')
87
+ end
88
+
89
+ class DesignValidator < SchemaValidator
90
+ def initialize = super(schema: 'design_schema.json')
91
+ end
92
+
93
+ class ToolingValidator < SchemaValidator
94
+ def initialize = super(schema: 'tooling_schema.json')
95
+ end
96
+ ```
97
+
98
+ Pros:
99
+
100
+ - DRY: shared validation logic in one place
101
+ - Extensible: future validators reuse the same base
102
+ - Existing API stays identical (`valid?`, `validate`, `errors`)
103
+ - Bug fixes apply once
104
+
105
+ Cons:
106
+
107
+ - Refactor touches `BehaviourValidator` (existing tests must still pass)
108
+ - Slightly more abstract (one level of inheritance)
109
+
110
+ ### Option C: Validation in compile step only
111
+
112
+ Validate documents only when the compiler reads them. No standalone command.
113
+
114
+ Pros:
115
+
116
+ - No new CLI command needed
117
+ - Validation happens where it matters
118
+
119
+ Cons:
120
+
121
+ - Errors only surface during compilation, not during authoring
122
+ - No way to validate documents without a compile cycle
123
+ - CI cannot validate documents independently of compilation
124
+
125
+ ## Decision
126
+
127
+ **Option B: Generic SchemaValidator** — because:
128
+
129
+ - DRY is justified: 3 validators today (behaviour, design, tooling), with
130
+ compiler and CC configuration confirmed for P2. This is not premature
131
+ abstraction — multiple consumers already exist.
132
+ - The existing `BehaviourValidator` API (`valid?`, `validate`, `errors`)
133
+ does not change. Existing tests continue to pass.
134
+ - Future validators become one-line subclasses.
135
+ - CI can validate each document category independently.
136
+
137
+ ## Acceptance criteria
138
+
139
+ 1. `SchemaValidator` base class has its own spec covering the full API
140
+ (`valid?`, `validate`, `errors`, error reset, missing file, bad YAML,
141
+ missing schema, invalid JSON schema)
142
+ 2. Every subclass has its own spec proving correct schema wiring and
143
+ successful validation of all real documents in its category
144
+ 3. For every `conf/schemas/*_schema.json` file, a corresponding validator
145
+ class exists in `lib/rosett_ai/validators/`, with a spec and a CI job
146
+ 4. For every validator class in `lib/rosett_ai/validators/`, a corresponding
147
+ schema file exists in `conf/schemas/` (no orphan validators)
148
+ 5. Existing `BehaviourValidator` spec passes unchanged after the refactor
149
+
150
+ ## Consequences
151
+
152
+ - Extract: `BehaviourValidator` logic moves to `SchemaValidator` base class
153
+ - `BehaviourValidator` becomes a thin wrapper inheriting from `SchemaValidator`
154
+ - New: `DesignValidator` wrapper inheriting from `SchemaValidator`
155
+ - New: `ToolingValidator` wrapper inheriting from `SchemaValidator`
156
+ - New CLI: `bin/raictl design validate [NAME]` — validates design docs
157
+ - New CLI: `bin/raictl tooling validate [NAME]` — validates tooling docs
158
+ - New CI jobs in validate stage for design and tooling documents
159
+ - Existing `BehaviourValidator` tests pass unchanged (same API)
160
+ - Future P2 validators (compiler, CC config) inherit from `SchemaValidator`
161
+ - Acceptance criteria 3-4 can be enforced by a CI check that compares
162
+ schema files against validator classes, ensuring neither side drifts
163
+
164
+ ### File changes
165
+
166
+ | Action | File |
167
+ |--------|------|
168
+ | New | `lib/rosett_ai/validators/schema_validator.rb` |
169
+ | Refactor | `lib/rosett_ai/validators/behaviour_validator.rb` (inherits SchemaValidator) |
170
+ | New | `lib/rosett_ai/validators/design_validator.rb` |
171
+ | New | `lib/rosett_ai/validators/tooling_validator.rb` |
172
+ | New | `lib/rosett_ai/thor/tasks/design.rb` |
173
+ | Modify | `lib/rosett_ai/thor/tasks/tooling.rb` (add validate subcommand) |
174
+ | New | `.gitlab-ci-files/validate/design-docs.yml` |
175
+ | New | `.gitlab-ci-files/validate/tooling-docs.yml` |
176
+ | Modify | `lib/rosett_ai/thor/cli.rb` (add design subcommand) |
177
+ | New | `spec/rosett_ai/validators/schema_validator_spec.rb` |
178
+ | New | `spec/rosett_ai/validators/design_validator_spec.rb` |
179
+ | New | `spec/rosett_ai/validators/tooling_validator_spec.rb` |