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.
- checksums.yaml +7 -0
- data/.ai-provenance.yml +119 -0
- data/.debride_whitelist +186 -0
- data/.fasterer.yml +29 -0
- data/.mdl_style.rb +10 -0
- data/.mdlrc +3 -0
- data/.mutant.yml +49 -0
- data/.namespace-allowlist +42 -0
- data/.reek.yml +1040 -0
- data/.rosett-ai/config.yml +3 -0
- data/.rspec +5 -0
- data/.rubocop.yml +380 -0
- data/.ruby-version +1 -0
- data/.yamllint +51 -0
- data/.yardopts +12 -0
- data/AI-DISCLOSURE.md +48 -0
- data/CHANGELOG.md +519 -0
- data/CLAUDE.md +141 -0
- data/CONTRIBUTING.md +734 -0
- data/INSTALL.md +154 -0
- data/LICENSE +674 -0
- data/LICENSE.md +675 -0
- data/QUICKSTART.md +73 -0
- data/README.md +366 -0
- data/Rakefile +200 -0
- data/SECURITY.md +114 -0
- data/bin/rai +1 -0
- data/cliff.toml +52 -0
- data/conf/adopt_redactions.yml +8 -0
- data/conf/behaviour/.gitkeep +0 -0
- data/conf/compliance/cra_rules.yml +25 -0
- data/conf/compliance/license_rules.yml +20 -0
- data/conf/design/aaif_alignment.yml +181 -0
- data/conf/design/ab_testing.yml +172 -0
- data/conf/design/accessibility.yml +84 -0
- data/conf/design/ai_authorship.yml +210 -0
- data/conf/design/ai_provenance.yml +224 -0
- data/conf/design/ai_tool_configuration.yml +207 -0
- data/conf/design/architecture.yml +139 -0
- data/conf/design/autocompletion.yml +115 -0
- data/conf/design/backward_compatibility.yml +112 -0
- data/conf/design/behaviour_composition.yml +246 -0
- data/conf/design/build_rake_extraction.yml +57 -0
- data/conf/design/ci_pipeline.yml +100 -0
- data/conf/design/claude_code_configuration.yml +157 -0
- data/conf/design/compiler.yml +128 -0
- data/conf/design/comply.yml +153 -0
- data/conf/design/content_packs.yml +84 -0
- data/conf/design/desktop_integration.yml +289 -0
- data/conf/design/distribution.yml +216 -0
- data/conf/design/doctor.yml +184 -0
- data/conf/design/documentation.yml +152 -0
- data/conf/design/engine_architecture.yml +257 -0
- data/conf/design/error_handling.yml +103 -0
- data/conf/design/feature_flags.yml +142 -0
- data/conf/design/git_hooks.yml +165 -0
- data/conf/design/gui_plugins.yml +475 -0
- data/conf/design/i18n.yml +84 -0
- data/conf/design/integration_testing.yml +56 -0
- data/conf/design/licensing_system.yml +88 -0
- data/conf/design/lifecycle_management.yml +208 -0
- data/conf/design/mcp_integration.yml +207 -0
- data/conf/design/mcp_settings.yml +126 -0
- data/conf/design/migration.yml +56 -0
- data/conf/design/monitoring_observability.yml +194 -0
- data/conf/design/namespace_cleanup.yml +145 -0
- data/conf/design/plugin_test_segregation.yml +145 -0
- data/conf/design/policy_management.yml +229 -0
- data/conf/design/project_management.yml +183 -0
- data/conf/design/rai_mcp_asset_discovery.yml +164 -0
- data/conf/design/rai_mcp_server.yml +605 -0
- data/conf/design/release_management.yml +117 -0
- data/conf/design/retrofit.yml +199 -0
- data/conf/design/retrospective_analyzer.yml +79 -0
- data/conf/design/scope_hierarchy.yml +352 -0
- data/conf/design/security.yml +115 -0
- data/conf/design/session_retrospective.yml +85 -0
- data/conf/design/smart_ui_feedback.yml +89 -0
- data/conf/design/structured_logging.yml +148 -0
- data/conf/design/styles.yml +123 -0
- data/conf/design/test_peer_review.yml +89 -0
- data/conf/design/testing.yml +136 -0
- data/conf/design/threat_model.yml +108 -0
- data/conf/design/ui_framework.yml +111 -0
- data/conf/design/usage_optimization.yml +122 -0
- data/conf/design/version_management.yml +60 -0
- data/conf/design/workflow.yml +227 -0
- data/conf/mcp/server_defaults.yml +42 -0
- data/conf/mcp/trust.yml +21 -0
- data/conf/packaging/core.yml +12 -0
- data/conf/packaging/gtk4.yml +11 -0
- data/conf/packaging/qt6.yml +11 -0
- data/conf/policy/default_deny_list.yml +197 -0
- data/conf/review/cli-command-audit.yml +857 -0
- data/conf/review/design-docs.yml +1064 -0
- data/conf/review/design-questionnaire.yml +153 -0
- data/conf/review/questionnaire.yml +146 -0
- data/conf/review/rosett-ai-core.yml +2919 -0
- data/conf/schemas/ai_config_schema.json +73 -0
- data/conf/schemas/behaviour_schema.json +132 -0
- data/conf/schemas/compliance_rule_schema.json +63 -0
- data/conf/schemas/content_pack_manifest_schema.json +51 -0
- data/conf/schemas/design_schema.json +210 -0
- data/conf/schemas/engine_manifest_schema.json +144 -0
- data/conf/schemas/lockfile_schema.json +74 -0
- data/conf/schemas/mcp_server_schema.json +48 -0
- data/conf/schemas/packaging_schema.json +70 -0
- data/conf/schemas/policy_schema.json +85 -0
- data/conf/schemas/provenance_schema.json +84 -0
- data/conf/schemas/rai_config_schema.json +56 -0
- data/conf/schemas/rai_project_schema.json +20 -0
- data/conf/schemas/scope_hierarchy_schema.json +49 -0
- data/conf/schemas/target_schema.json +67 -0
- data/conf/schemas/tooling_schema.json +65 -0
- data/conf/schemas/workflow_schema.json +112 -0
- data/conf/targets/agents_md.yml +17 -0
- data/conf/targets/claude.yml +12 -0
- data/conf/tooling/tools.yml +58 -0
- data/dist/rosett-ai-mcp.service +48 -0
- data/dist/rosett-ai-mcp.yml.default +45 -0
- data/doc/AAIF_POSITIONING.md +58 -0
- data/doc/ADOPT.md +224 -0
- data/doc/AI_PROVENANCE.md +139 -0
- data/doc/ARCHITECTURE.md +920 -0
- data/doc/BEHAVIOUR.md +409 -0
- data/doc/BUILD.md +138 -0
- data/doc/CI_CD_RECIPES.md +171 -0
- data/doc/CLAUDE_SESSIONS_MOVED.md +16 -0
- data/doc/COMMAND_ANALYSIS.md +229 -0
- data/doc/CONFIGURATION.md +281 -0
- data/doc/DESIGN_AUDIT.md +235 -0
- data/doc/DESIGN_PEER_REVIEW.md +771 -0
- data/doc/DESKTOP.md +447 -0
- data/doc/ENGINES.md +567 -0
- data/doc/ENGINE_DEVELOPMENT_GUIDE.md +417 -0
- data/doc/FEATURE_AUDIT.md +218 -0
- data/doc/IMPLEMENTATION_PLAN.md +669 -0
- data/doc/INCIDENT_REPORT_2026-02-02.md +251 -0
- data/doc/MIGRATION_GUIDE.md +88 -0
- data/doc/PACKAGING.md +232 -0
- data/doc/PROJECT_DASHBOARD.md +153 -0
- data/doc/PULP_DEPLOYMENT.md +164 -0
- data/doc/QUALITY_FIX_SUMMARY.md +110 -0
- data/doc/QUICK_START.md +162 -0
- data/doc/REEK_CONFIGURATION.md +166 -0
- data/doc/REFERENCE.md +253 -0
- data/doc/REFERENCES.md +324 -0
- data/doc/SECURITY_REVIEW_CHECKLIST.md +72 -0
- data/doc/SESSION_2026-02-28_GTK4_HARDENING.md +359 -0
- data/doc/SETUP.md +202 -0
- data/doc/TEST_PEER_REVIEW.md +152 -0
- data/doc/THREAT_MODEL.md +230 -0
- data/doc/USAGE.md +545 -0
- data/doc/USER_MANUAL.md +585 -0
- data/doc/ai_test_review_checklist.md +110 -0
- data/doc/changes/2026-02-18-packaging-fpm.md +155 -0
- data/doc/changes/2026-02-19-testing-infrastructure.md +221 -0
- data/doc/changes/2026-02-20-security-implementation.md +281 -0
- data/doc/changes/2026-02-20-styles-implementation.md +220 -0
- data/doc/changes/2026-02-21-architecture-completion.md +95 -0
- data/doc/changes/2026-02-21-architecture-ui-layer.md +253 -0
- data/doc/changes/2026-02-21-cc-config-implementation.md +108 -0
- data/doc/changes/2026-02-21-ci-pipeline-implementation.md +214 -0
- data/doc/changes/2026-02-21-compiler-multi-target-pipeline.md +241 -0
- data/doc/changes/2026-02-21-config-design-show-commands.md +61 -0
- data/doc/changes/2026-02-21-design-implementation-overview.md +455 -0
- data/doc/changes/2026-02-21-lifecycle-management.md +196 -0
- data/doc/changes/2026-02-21-path-resolver.md +128 -0
- data/doc/changes/2026-02-24-ci-tmpdir-mutant-fetch.md +45 -0
- data/doc/changes/2026-03-01-ci-bundler-strategy.md +120 -0
- data/doc/changes/2026-03-20-security-hardening-phase2.md +163 -0
- data/doc/context/SESSION-HANDOFF.md +69 -0
- data/doc/context/ai-engine-usage-trends-2026.md +80 -0
- data/doc/context/plan-pluggable-engines.md +590 -0
- data/doc/decisions/001-flog-deferred.md +32 -0
- data/doc/decisions/002-path-resolution-strategy.md +158 -0
- data/doc/decisions/003-ui-adapter-selection.md +193 -0
- data/doc/decisions/004-design-document-validation.md +179 -0
- data/doc/decisions/005-package-splitting-strategy.md +200 -0
- data/doc/decisions/006-multi-engine-architecture.md +147 -0
- data/doc/decisions/007-engine-agnostic-pivot.md +219 -0
- data/doc/decisions/008-ci-bundler-strategy.md +129 -0
- data/doc/decisions/009-core-only-v1-release.md +60 -0
- data/doc/decisions/010-engine-debian-packaging.md +66 -0
- data/doc/decisions/011-context-aware-cli.md +71 -0
- data/doc/dependency_decisions.yml +247 -0
- data/doc/issues/001-wrapper-missing-environment-variables.md +197 -0
- data/doc/issues/002-embedded-ruby-wrong-prefix.md +217 -0
- data/doc/issues/003-smoke-test-false-positive.md +127 -0
- data/doc/issues/004-market-research-design-updates.md +109 -0
- data/doc/issues/005-compile-scope-coexistence.md +161 -0
- data/doc/locales/.gitkeep +0 -0
- data/doc/man/rai.1.ronn +505 -0
- data/doc/operations/packaging.md +133 -0
- data/doc/operations/rosett-ai-release.md +65 -0
- data/doc/reference/error-catalog.md +107 -0
- data/doc/reference/rosett-ai-technical-reference.pdf +0 -0
- data/doc/reference/src/Pictures/cover.jpg +0 -0
- data/doc/reference/src/Pictures/head1.jpg +0 -0
- data/doc/reference/src/Pictures/head2.jpg +0 -0
- data/doc/reference/src/Pictures/head3.jpg +0 -0
- data/doc/reference/src/Pictures/head4.jpg +0 -0
- data/doc/reference/src/Pictures/head5.jpg +0 -0
- data/doc/reference/src/Pictures/head6.jpg +0 -0
- data/doc/reference/src/Pictures/head7.jpg +0 -0
- data/doc/reference/src/Pictures/head8.jpg +0 -0
- data/doc/reference/src/StyleInd.ist +4 -0
- data/doc/reference/src/bibliography.bib +79 -0
- data/doc/reference/src/main.tex +1288 -0
- data/doc/reference/src/structure.tex +303 -0
- data/doc/rosett-ai-bookmarks.html +301 -0
- data/kitchen.yml +46 -0
- data/lib/rosett_ai/adopter/executor_resolver.rb +77 -0
- data/lib/rosett_ai/adopter/local_analysis_collector.rb +154 -0
- data/lib/rosett_ai/adopter/rule_adopter.rb +254 -0
- data/lib/rosett_ai/ai_config/config_compiler.rb +111 -0
- data/lib/rosett_ai/ai_config/context_window.rb +55 -0
- data/lib/rosett_ai/ai_config/cost_controls.rb +44 -0
- data/lib/rosett_ai/ai_config/fallback_chain.rb +64 -0
- data/lib/rosett_ai/ai_config/model_router.rb +121 -0
- data/lib/rosett_ai/ai_config/validator.rb +45 -0
- data/lib/rosett_ai/authorship/attribution_compiler.rb +99 -0
- data/lib/rosett_ai/authorship/disclosure_policy.rb +81 -0
- data/lib/rosett_ai/authorship/review_validator.rb +39 -0
- data/lib/rosett_ai/authorship/trailer_generator.rb +88 -0
- data/lib/rosett_ai/backup/compressor.rb +180 -0
- data/lib/rosett_ai/backup/destination.rb +91 -0
- data/lib/rosett_ai/behaviour/manager.rb +156 -0
- data/lib/rosett_ai/compiler/backend.rb +86 -0
- data/lib/rosett_ai/compiler/backends/agents_md_backend.rb +80 -0
- data/lib/rosett_ai/compiler/backends/claude_backend.rb +88 -0
- data/lib/rosett_ai/compiler/backends/generic_backend.rb +15 -0
- data/lib/rosett_ai/compiler/behaviour_compiler.rb +40 -0
- data/lib/rosett_ai/compiler/capability_checker.rb +104 -0
- data/lib/rosett_ai/compiler/compilation_pipeline.rb +361 -0
- data/lib/rosett_ai/compiler/compiled_output.rb +39 -0
- data/lib/rosett_ai/compiler/locale_compiler.rb +250 -0
- data/lib/rosett_ai/compiler/target_profile.rb +112 -0
- data/lib/rosett_ai/completion/generator.rb +101 -0
- data/lib/rosett_ai/completion/shells/bash_generator.rb +126 -0
- data/lib/rosett_ai/completion/shells/fish_generator.rb +78 -0
- data/lib/rosett_ai/completion/shells/zsh_generator.rb +126 -0
- data/lib/rosett_ai/comply/checkers/cra_checker.rb +102 -0
- data/lib/rosett_ai/comply/checkers/license_checker.rb +85 -0
- data/lib/rosett_ai/comply/checkers/spdx_header_checker.rb +98 -0
- data/lib/rosett_ai/comply/reporter.rb +113 -0
- data/lib/rosett_ai/comply/runner.rb +50 -0
- data/lib/rosett_ai/composition/circular_dependency_detector.rb +56 -0
- data/lib/rosett_ai/composition/composer.rb +158 -0
- data/lib/rosett_ai/composition/composition_result.rb +64 -0
- data/lib/rosett_ai/composition/conflict_detector.rb +53 -0
- data/lib/rosett_ai/composition/lockfile.rb +103 -0
- data/lib/rosett_ai/composition/merge_strategy.rb +131 -0
- data/lib/rosett_ai/composition/priority_sorter.rb +29 -0
- data/lib/rosett_ai/composition/scope_resolver.rb +55 -0
- data/lib/rosett_ai/config/compile_result.rb +37 -0
- data/lib/rosett_ai/config/compiler.rb +13 -0
- data/lib/rosett_ai/config/domain_transformer.rb +13 -0
- data/lib/rosett_ai/config/key_map.rb +13 -0
- data/lib/rosett_ai/config/masking_secret_resolver.rb +40 -0
- data/lib/rosett_ai/config/scope_router.rb +13 -0
- data/lib/rosett_ai/config/secret_resolver.rb +125 -0
- data/lib/rosett_ai/configuration.rb +119 -0
- data/lib/rosett_ai/content/content_client.rb +60 -0
- data/lib/rosett_ai/content/pack_installer.rb +117 -0
- data/lib/rosett_ai/content/pack_manifest.rb +50 -0
- data/lib/rosett_ai/content/pack_registry.rb +68 -0
- data/lib/rosett_ai/content_packs/manager.rb +50 -0
- data/lib/rosett_ai/dbus/compositor_detector.rb +77 -0
- data/lib/rosett_ai/dbus/focus_adapters/base.rb +59 -0
- data/lib/rosett_ai/dbus/focus_adapters/gnome_adapter.rb +172 -0
- data/lib/rosett_ai/dbus/focus_adapters/hyprland_adapter.rb +77 -0
- data/lib/rosett_ai/dbus/focus_adapters/i3_adapter.rb +65 -0
- data/lib/rosett_ai/dbus/focus_adapters/kwin_adapter.rb +103 -0
- data/lib/rosett_ai/dbus/focus_adapters/x11_adapter.rb +105 -0
- data/lib/rosett_ai/dbus/focus_monitor_interface.rb +103 -0
- data/lib/rosett_ai/dbus/manager_interface.rb +213 -0
- data/lib/rosett_ai/dbus/plugin_manager_interface.rb +169 -0
- data/lib/rosett_ai/dbus/rate_limiter.rb +89 -0
- data/lib/rosett_ai/dbus/service.rb +121 -0
- data/lib/rosett_ai/dbus/status_notifier_interface.rb +79 -0
- data/lib/rosett_ai/deprecation.rb +79 -0
- data/lib/rosett_ai/desktop/dbus_client.rb +259 -0
- data/lib/rosett_ai/desktop/gtk4_app.rb +371 -0
- data/lib/rosett_ai/desktop/gtk4_preferences.rb +331 -0
- data/lib/rosett_ai/desktop/gui_logger.rb +236 -0
- data/lib/rosett_ai/doctor/check.rb +92 -0
- data/lib/rosett_ai/doctor/checks/cache_health_check.rb +50 -0
- data/lib/rosett_ai/doctor/checks/dbus_availability_check.rb +39 -0
- data/lib/rosett_ai/doctor/checks/engine_detection_check.rb +46 -0
- data/lib/rosett_ai/doctor/checks/file_permission_check.rb +44 -0
- data/lib/rosett_ai/doctor/checks/gem_dependency_check.rb +55 -0
- data/lib/rosett_ai/doctor/checks/ruby_version_check.rb +50 -0
- data/lib/rosett_ai/doctor/checks/stale_config_nncc_check.rb +57 -0
- data/lib/rosett_ai/doctor/checks/stale_home_nncc_check.rb +59 -0
- data/lib/rosett_ai/doctor.rb +81 -0
- data/lib/rosett_ai/documentation/reference_compiler.rb +122 -0
- data/lib/rosett_ai/documentation/translator.rb +62 -0
- data/lib/rosett_ai/engines/base_config_compiler.rb +203 -0
- data/lib/rosett_ai/engines/detector.rb +63 -0
- data/lib/rosett_ai/engines/registry.rb +50 -0
- data/lib/rosett_ai/error_handler.rb +139 -0
- data/lib/rosett_ai/exit_codes.rb +76 -0
- data/lib/rosett_ai/feature_flags.rb +102 -0
- data/lib/rosett_ai/formatting.rb +33 -0
- data/lib/rosett_ai/gem_consistency_checker.rb +199 -0
- data/lib/rosett_ai/git_hooks/chain_detector.rb +86 -0
- data/lib/rosett_ai/git_hooks/installer.rb +175 -0
- data/lib/rosett_ai/git_hooks/script_generator.rb +125 -0
- data/lib/rosett_ai/gitlab/validators/supplementary_gitlab_ci_yaml_validator.rb +79 -0
- data/lib/rosett_ai/i18n/locale_resolver.rb +46 -0
- data/lib/rosett_ai/i18n/utf8_checker.rb +32 -0
- data/lib/rosett_ai/init/config_file_writer.rb +24 -0
- data/lib/rosett_ai/init/directory_builder.rb +38 -0
- data/lib/rosett_ai/init/file_copier.rb +95 -0
- data/lib/rosett_ai/init/global_initializer.rb +28 -0
- data/lib/rosett_ai/init/local_initializer.rb +27 -0
- data/lib/rosett_ai/init/mcp_registrar.rb +109 -0
- data/lib/rosett_ai/init/project_initializer.rb +38 -0
- data/lib/rosett_ai/licensing/license_key.rb +139 -0
- data/lib/rosett_ai/licensing/license_store.rb +64 -0
- data/lib/rosett_ai/licensing/license_validator.rb +60 -0
- data/lib/rosett_ai/licensing/tier.rb +42 -0
- data/lib/rosett_ai/mcp/admin/auditor.rb +88 -0
- data/lib/rosett_ai/mcp/admin/health_checker.rb +81 -0
- data/lib/rosett_ai/mcp/admin/registry.rb +100 -0
- data/lib/rosett_ai/mcp/admin/schema_validator.rb +63 -0
- data/lib/rosett_ai/mcp/enforcement/.gitkeep +0 -0
- data/lib/rosett_ai/mcp/enforcement/hook_generator.rb +197 -0
- data/lib/rosett_ai/mcp/enforcement/validator.rb +215 -0
- data/lib/rosett_ai/mcp/governance.rb +160 -0
- data/lib/rosett_ai/mcp/http_security_config.rb +158 -0
- data/lib/rosett_ai/mcp/instructions.rb +266 -0
- data/lib/rosett_ai/mcp/key_hasher.rb +66 -0
- data/lib/rosett_ai/mcp/keyfile.rb +221 -0
- data/lib/rosett_ai/mcp/middleware/authentication.rb +146 -0
- data/lib/rosett_ai/mcp/middleware/content_type.rb +56 -0
- data/lib/rosett_ai/mcp/middleware/cors.rb +83 -0
- data/lib/rosett_ai/mcp/middleware/origin_validation.rb +73 -0
- data/lib/rosett_ai/mcp/middleware/rate_limit.rb +106 -0
- data/lib/rosett_ai/mcp/middleware/request_size.rb +51 -0
- data/lib/rosett_ai/mcp/plugins.rb +143 -0
- data/lib/rosett_ai/mcp/prompts/compilation_prompt.rb +40 -0
- data/lib/rosett_ai/mcp/prompts/compliance_prompt.rb +41 -0
- data/lib/rosett_ai/mcp/prompts/diagnostics_prompt.rb +41 -0
- data/lib/rosett_ai/mcp/prompts/validation_prompt.rb +41 -0
- data/lib/rosett_ai/mcp/resources/behaviour_resource.rb +127 -0
- data/lib/rosett_ai/mcp/resources/config_resource.rb +72 -0
- data/lib/rosett_ai/mcp/resources/design_resource.rb +58 -0
- data/lib/rosett_ai/mcp/resources/hooks_resource.rb +74 -0
- data/lib/rosett_ai/mcp/resources/provenance_resource.rb +51 -0
- data/lib/rosett_ai/mcp/resources/rules_resource.rb +60 -0
- data/lib/rosett_ai/mcp/resources/schema_resource.rb +72 -0
- data/lib/rosett_ai/mcp/response_helper.rb +46 -0
- data/lib/rosett_ai/mcp/security_logger.rb +60 -0
- data/lib/rosett_ai/mcp/server.rb +212 -0
- data/lib/rosett_ai/mcp/settings/server_installer.rb +112 -0
- data/lib/rosett_ai/mcp/settings/trust_manager.rb +142 -0
- data/lib/rosett_ai/mcp/tools/adopt_tool.rb +70 -0
- data/lib/rosett_ai/mcp/tools/backup_tool.rb +64 -0
- data/lib/rosett_ai/mcp/tools/behaviour_display_tool.rb +72 -0
- data/lib/rosett_ai/mcp/tools/behaviour_list_tool.rb +56 -0
- data/lib/rosett_ai/mcp/tools/behaviour_manage_tool.rb +114 -0
- data/lib/rosett_ai/mcp/tools/behaviour_show_tool.rb +62 -0
- data/lib/rosett_ai/mcp/tools/compile_status_tool.rb +122 -0
- data/lib/rosett_ai/mcp/tools/compile_tool.rb +191 -0
- data/lib/rosett_ai/mcp/tools/comply_tool.rb +79 -0
- data/lib/rosett_ai/mcp/tools/config_compile_tool.rb +71 -0
- data/lib/rosett_ai/mcp/tools/config_status_tool.rb +79 -0
- data/lib/rosett_ai/mcp/tools/content_tool.rb +78 -0
- data/lib/rosett_ai/mcp/tools/context_query_tool.rb +156 -0
- data/lib/rosett_ai/mcp/tools/design_list_tool.rb +57 -0
- data/lib/rosett_ai/mcp/tools/design_show_tool.rb +69 -0
- data/lib/rosett_ai/mcp/tools/doctor_tool.rb +62 -0
- data/lib/rosett_ai/mcp/tools/documentation_status_tool.rb +45 -0
- data/lib/rosett_ai/mcp/tools/engines_tool.rb +84 -0
- data/lib/rosett_ai/mcp/tools/hook_install_tool.rb +190 -0
- data/lib/rosett_ai/mcp/tools/hook_preview_tool.rb +173 -0
- data/lib/rosett_ai/mcp/tools/hooks_status_tool.rb +84 -0
- data/lib/rosett_ai/mcp/tools/init_tool.rb +87 -0
- data/lib/rosett_ai/mcp/tools/license_status_tool.rb +44 -0
- data/lib/rosett_ai/mcp/tools/project_tool.rb +117 -0
- data/lib/rosett_ai/mcp/tools/provenance_tool.rb +97 -0
- data/lib/rosett_ai/mcp/tools/provenance_write_tool.rb +40 -0
- data/lib/rosett_ai/mcp/tools/retrofit_tool.rb +81 -0
- data/lib/rosett_ai/mcp/tools/rule_search_tool.rb +163 -0
- data/lib/rosett_ai/mcp/tools/schema_get_tool.rb +94 -0
- data/lib/rosett_ai/mcp/tools/tooling_tool.rb +86 -0
- data/lib/rosett_ai/mcp/tools/validate_tool.rb +105 -0
- data/lib/rosett_ai/mcp/tools/workflow_execute_tool.rb +74 -0
- data/lib/rosett_ai/mcp/tools/workflow_tool.rb +78 -0
- data/lib/rosett_ai/migration/detector.rb +117 -0
- data/lib/rosett_ai/migration/nncc_config_migrator.rb +94 -0
- data/lib/rosett_ai/migration/nncc_project_migrator.rb +90 -0
- data/lib/rosett_ai/migration/xdg_migrator.rb +123 -0
- data/lib/rosett_ai/package_manager/apt.rb +108 -0
- data/lib/rosett_ai/package_manager/base.rb +68 -0
- data/lib/rosett_ai/package_manager/gem_backend.rb +90 -0
- data/lib/rosett_ai/packaging/variant_config.rb +92 -0
- data/lib/rosett_ai/path_resolver.rb +115 -0
- data/lib/rosett_ai/plugins/contract.rb +43 -0
- data/lib/rosett_ai/plugins/engine_contract.rb +60 -0
- data/lib/rosett_ai/plugins/gui_contract.rb +74 -0
- data/lib/rosett_ai/plugins/mcp_contract.rb +48 -0
- data/lib/rosett_ai/plugins/registry.rb +150 -0
- data/lib/rosett_ai/policy/auditor.rb +41 -0
- data/lib/rosett_ai/policy/deny_list.rb +71 -0
- data/lib/rosett_ai/policy/opt_out_scanner.rb +37 -0
- data/lib/rosett_ai/policy/policy_compiler.rb +84 -0
- data/lib/rosett_ai/policy/protected_files.rb +47 -0
- data/lib/rosett_ai/policy/tier_hierarchy.rb +48 -0
- data/lib/rosett_ai/policy/validator.rb +35 -0
- data/lib/rosett_ai/profiler.rb +79 -0
- data/lib/rosett_ai/project/drift_detector.rb +126 -0
- data/lib/rosett_ai/project/manager.rb +115 -0
- data/lib/rosett_ai/project/sync_manager.rb +138 -0
- data/lib/rosett_ai/project/template_applier.rb +105 -0
- data/lib/rosett_ai/project_context.rb +82 -0
- data/lib/rosett_ai/provenance/entry.rb +63 -0
- data/lib/rosett_ai/provenance/file_source.rb +32 -0
- data/lib/rosett_ai/provenance/source.rb +62 -0
- data/lib/rosett_ai/provenance/store.rb +153 -0
- data/lib/rosett_ai/provenance/tracker.rb +62 -0
- data/lib/rosett_ai/provenance/trailer_generator.rb +43 -0
- data/lib/rosett_ai/provenance/validator.rb +45 -0
- data/lib/rosett_ai/quorum/collector.rb +59 -0
- data/lib/rosett_ai/quorum/comparator.rb +81 -0
- data/lib/rosett_ai/quorum/dispatcher.rb +57 -0
- data/lib/rosett_ai/quorum/strategies/adopt.rb +56 -0
- data/lib/rosett_ai/rai_config.rb +107 -0
- data/lib/rosett_ai/retrofit/base_parser.rb +66 -0
- data/lib/rosett_ai/retrofit/engine.rb +171 -0
- data/lib/rosett_ai/retrofit/parsers/agents_md_parser.rb +50 -0
- data/lib/rosett_ai/retrofit/parsers/claude_parser.rb +69 -0
- data/lib/rosett_ai/retrofit/parsers/cursor_parser.rb +82 -0
- data/lib/rosett_ai/retrofit/round_trip_validator.rb +65 -0
- data/lib/rosett_ai/retrofit/scanner.rb +47 -0
- data/lib/rosett_ai/retrofit/secret_detector.rb +87 -0
- data/lib/rosett_ai/secrets_resolver.rb +71 -0
- data/lib/rosett_ai/smart_feedback/suggester.rb +83 -0
- data/lib/rosett_ai/smart_feedback/thor_middleware.rb +84 -0
- data/lib/rosett_ai/structured_logger.rb +110 -0
- data/lib/rosett_ai/telemetry/json_lines_writer.rb +50 -0
- data/lib/rosett_ai/telemetry/log_rotator.rb +67 -0
- data/lib/rosett_ai/telemetry/provider.rb +26 -0
- data/lib/rosett_ai/telemetry/reporter.rb +144 -0
- data/lib/rosett_ai/telemetry.rb +47 -0
- data/lib/rosett_ai/text_sanitizer.rb +62 -0
- data/lib/rosett_ai/thor/cli.rb +269 -0
- data/lib/rosett_ai/thor/tasks/adopt.rb +250 -0
- data/lib/rosett_ai/thor/tasks/backup.rb +420 -0
- data/lib/rosett_ai/thor/tasks/behaviour.rb +474 -0
- data/lib/rosett_ai/thor/tasks/build.rb +1162 -0
- data/lib/rosett_ai/thor/tasks/compile.rb +415 -0
- data/lib/rosett_ai/thor/tasks/completion.rb +123 -0
- data/lib/rosett_ai/thor/tasks/comply.rb +82 -0
- data/lib/rosett_ai/thor/tasks/config.rb +265 -0
- data/lib/rosett_ai/thor/tasks/content.rb +193 -0
- data/lib/rosett_ai/thor/tasks/dbus.rb +321 -0
- data/lib/rosett_ai/thor/tasks/design.rb +258 -0
- data/lib/rosett_ai/thor/tasks/desktop.rb +129 -0
- data/lib/rosett_ai/thor/tasks/doctor.rb +127 -0
- data/lib/rosett_ai/thor/tasks/documentation.rb +321 -0
- data/lib/rosett_ai/thor/tasks/engines.rb +167 -0
- data/lib/rosett_ai/thor/tasks/hooks.rb +219 -0
- data/lib/rosett_ai/thor/tasks/init.rb +259 -0
- data/lib/rosett_ai/thor/tasks/license.rb +120 -0
- data/lib/rosett_ai/thor/tasks/mcp.rb +535 -0
- data/lib/rosett_ai/thor/tasks/migrate.rb +121 -0
- data/lib/rosett_ai/thor/tasks/plugins.rb +157 -0
- data/lib/rosett_ai/thor/tasks/project.rb +260 -0
- data/lib/rosett_ai/thor/tasks/provenance.rb +195 -0
- data/lib/rosett_ai/thor/tasks/release.rb +314 -0
- data/lib/rosett_ai/thor/tasks/retrofit.rb +90 -0
- data/lib/rosett_ai/thor/tasks/tooling.rb +308 -0
- data/lib/rosett_ai/thor/tasks/validate.rb +108 -0
- data/lib/rosett_ai/thor/tasks/workflow.rb +196 -0
- data/lib/rosett_ai/tooling/ci_yaml_validator.rb +37 -0
- data/lib/rosett_ai/tooling/version_checker.rb +35 -0
- data/lib/rosett_ai/ui/accessible_tui.rb +61 -0
- data/lib/rosett_ai/ui/base.rb +46 -0
- data/lib/rosett_ai/ui/gtk4.rb +98 -0
- data/lib/rosett_ai/ui/kde.rb +40 -0
- data/lib/rosett_ai/ui/qt6.rb +40 -0
- data/lib/rosett_ai/ui/registry.rb +60 -0
- data/lib/rosett_ai/ui/tty_helper.rb +74 -0
- data/lib/rosett_ai/ui/tui.rb +59 -0
- data/lib/rosett_ai/validators/behaviour_validator.rb +20 -0
- data/lib/rosett_ai/validators/design_validator.rb +17 -0
- data/lib/rosett_ai/validators/schema_validator.rb +84 -0
- data/lib/rosett_ai/validators/tooling_validator.rb +17 -0
- data/lib/rosett_ai/version.rb +8 -0
- data/lib/rosett_ai/version_consistency_checker.rb +129 -0
- data/lib/rosett_ai/workflow/audit_log.rb +86 -0
- data/lib/rosett_ai/workflow/engine.rb +142 -0
- data/lib/rosett_ai/workflow/manager.rb +82 -0
- data/lib/rosett_ai/workflow/schema_validator.rb +71 -0
- data/lib/rosett_ai/workflow/step_runner.rb +61 -0
- data/lib/rosett_ai/workflow/steps/prompt_step.rb +62 -0
- data/lib/rosett_ai/workflow/steps/rai_step.rb +74 -0
- data/lib/rosett_ai/workflow/steps/shell_step.rb +53 -0
- data/lib/rosett_ai/yaml_loader.rb +78 -0
- data/lib/rosett_ai.rb +221 -0
- data/lib/rubocop/cop/rosett_ai/shell_interpolation.rb +54 -0
- data/lib/rubocop/cop/rosett_ai/unsafe_const_get.rb +60 -0
- data/lib/rubocop/cop/rosett_ai/unsafe_send.rb +50 -0
- data/lib/rubocop/cop/rosett_ai/unsafe_yaml_load.rb +40 -0
- data/lib/rubocop/rosett_ai.rb +9 -0
- data/lib/scripts/generated/docker_hub_tags.rb +126 -0
- data/locales/.gitkeep +0 -0
- data/locales/ar.yml +579 -0
- data/locales/en.yml +571 -0
- data/locales/fr.yml +567 -0
- data/packaging/build-engine-deb.sh +81 -0
- data/packaging/scripts/postinst +17 -0
- data/packaging/scripts/postrm +19 -0
- data/packaging/scripts/prerm +10 -0
- data/packaging/wrapper.sh.template +38 -0
- data/rosett-ai.gemspec +63 -0
- data/rules/.gitkeep +0 -0
- data/scripts/publish/pulp_upload.sh +123 -0
- data/settings.json +29 -0
- data/share/applications/be.neatnerds.rosettai.desktop +29 -0
- data/share/dbus-1/interfaces/be.neatnerds.rosettai.xml +103 -0
- data/share/dbus-1/services/be.neatnerds.rosettai.service +3 -0
- data/share/templates/behaviour/criticalthinking.yml +69 -0
- metadata +810 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# ADR-005: Package Splitting Strategy
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted (2026-02-21) — implementation deferred to P3/P4
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
The `architecture.yml` design document requires that GUI frontends are
|
|
10
|
+
delivered as separate Debian packages (`rosett-ai-gtk4`, `rosett-ai-qt6`, `rosett-ai-kde`)
|
|
11
|
+
that depend on the core `rosett-ai` package. The core must be fully functional
|
|
12
|
+
without any GUI package installed.
|
|
13
|
+
|
|
14
|
+
The `RosettAi::Ui::Registry` pattern is already in place. Adapters register
|
|
15
|
+
themselves at require time, and `Registry.resolve(name)` instantiates the
|
|
16
|
+
requested adapter. The TUI adapter is the default and the only adapter
|
|
17
|
+
currently implemented.
|
|
18
|
+
|
|
19
|
+
The build infrastructure exists: fpm-based `.deb` packaging via
|
|
20
|
+
`bin/raictl build package`.
|
|
21
|
+
|
|
22
|
+
The question is how to organise the source code for multiple packages.
|
|
23
|
+
|
|
24
|
+
## Options
|
|
25
|
+
|
|
26
|
+
### Option A: Monorepo with multiple gemspecs
|
|
27
|
+
|
|
28
|
+
Single Git repository with a directory per package:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
rosett-ai/
|
|
32
|
+
├── rosett-ai.gemspec # Core + TUI
|
|
33
|
+
├── packages/
|
|
34
|
+
│ ├── rosett-ai-gtk4/
|
|
35
|
+
│ │ ├── rosett-ai-gtk4.gemspec
|
|
36
|
+
│ │ └── lib/rosett_ai/ui/gtk4.rb
|
|
37
|
+
│ ├── rosett-ai-qt6/
|
|
38
|
+
│ │ ├── rosett-ai-qt6.gemspec
|
|
39
|
+
│ │ └── lib/rosett_ai/ui/qt6.rb
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Pros:
|
|
43
|
+
|
|
44
|
+
- Atomic cross-package changes (interface + adapter in one commit)
|
|
45
|
+
- Single CI pipeline validates all packages together
|
|
46
|
+
- Shared test infrastructure
|
|
47
|
+
|
|
48
|
+
Cons:
|
|
49
|
+
|
|
50
|
+
- Repository grows with each GUI toolkit's dependencies
|
|
51
|
+
- CI must install GTK4, Qt6, and KDE libraries to test all packages
|
|
52
|
+
- fpm build configuration becomes complex
|
|
53
|
+
- Contributors must install all toolkit dev packages
|
|
54
|
+
|
|
55
|
+
### Option B: Separate repositories
|
|
56
|
+
|
|
57
|
+
Core stays in this repository. Each GUI package gets its own repository
|
|
58
|
+
(`rosett-ai-gtk4`, `rosett-ai-qt6`).
|
|
59
|
+
|
|
60
|
+
Pros:
|
|
61
|
+
|
|
62
|
+
- Clean separation of concerns
|
|
63
|
+
- Independent CI pipelines with toolkit-specific dependencies
|
|
64
|
+
- Independent release cycles
|
|
65
|
+
|
|
66
|
+
Cons:
|
|
67
|
+
|
|
68
|
+
- Cross-repo coordination when Base interface changes
|
|
69
|
+
- Version drift risk between core and GUI packages
|
|
70
|
+
- Harder to run shared examples across repos
|
|
71
|
+
- Changes to shared_examples require multi-repo updates
|
|
72
|
+
|
|
73
|
+
### Option C: Plugin gems within core repo
|
|
74
|
+
|
|
75
|
+
GUI adapter files live in the core repository under `lib/rosett_ai/ui/` but are
|
|
76
|
+
excluded from the core `rosett-ai.gemspec`. Each plugin has its own gemspec at the
|
|
77
|
+
repo root. Both core and plugins are built from the same repository but as
|
|
78
|
+
separate gems and separate `.deb` packages.
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
rosett-ai/
|
|
82
|
+
├── rosett-ai.gemspec # Core: lib/rosett_ai/ui/{base,registry,tui}.rb
|
|
83
|
+
├── rosett-ai-gtk4.gemspec # Plugin: lib/rosett_ai/ui/gtk4.rb
|
|
84
|
+
├── rosett-ai-qt6.gemspec # Plugin: lib/rosett_ai/ui/qt6.rb
|
|
85
|
+
├── lib/rosett_ai/ui/
|
|
86
|
+
│ ├── base.rb # Shipped in rosett-ai
|
|
87
|
+
│ ├── registry.rb # Shipped in rosett-ai
|
|
88
|
+
│ ├── tui.rb # Shipped in rosett-ai
|
|
89
|
+
│ ├── gtk4.rb # Shipped in rosett-ai-gtk4
|
|
90
|
+
│ └── qt6.rb # Shipped in rosett-ai-qt6
|
|
91
|
+
├── spec/rosett_ai/ui/
|
|
92
|
+
│ ├── base_spec.rb
|
|
93
|
+
│ ├── tui_spec.rb
|
|
94
|
+
│ ├── gtk4_spec.rb # Runs shared_examples
|
|
95
|
+
│ └── qt6_spec.rb # Runs shared_examples
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Auto-registration at the bottom of each adapter file:
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
# lib/rosett_ai/ui/gtk4.rb
|
|
102
|
+
RosettAi::Ui::Registry.register(:gtk4, Gtk4)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Debian packages:
|
|
106
|
+
|
|
107
|
+
- `rosett-ai` ships `lib/rosett_ai/ui/{base,registry,tui}.rb` + all core
|
|
108
|
+
- `rosett-ai-gtk4` ships `lib/rosett_ai/ui/gtk4.rb`, depends on `raictl (>= X.Y.Z)`
|
|
109
|
+
and `libgtk-4-dev`
|
|
110
|
+
- `rosett-ai-qt6` ships `lib/rosett_ai/ui/qt6.rb`, depends on `raictl (>= X.Y.Z)`
|
|
111
|
+
and `qt6-base-dev`
|
|
112
|
+
|
|
113
|
+
Pros:
|
|
114
|
+
|
|
115
|
+
- Shared examples validate all adapters against the same interface contract
|
|
116
|
+
- One CI pipeline — test matrix per toolkit (optional stages)
|
|
117
|
+
- Interface changes and adapter updates in the same commit
|
|
118
|
+
- `Registry.register` call in each adapter file provides auto-discovery
|
|
119
|
+
- fpm can build multiple `.deb` packages from the same source tree
|
|
120
|
+
|
|
121
|
+
Cons:
|
|
122
|
+
|
|
123
|
+
- Gemspec file exclusion lists must be maintained carefully
|
|
124
|
+
- CI needs conditional toolkit installation (only for GUI test stages)
|
|
125
|
+
- Contributors see all adapter code even if they only work on one
|
|
126
|
+
|
|
127
|
+
## Decision
|
|
128
|
+
|
|
129
|
+
**Option C: Plugin gems within core repository** — because:
|
|
130
|
+
|
|
131
|
+
- The shared_examples pattern (`'a UI implementation'`) is already in place
|
|
132
|
+
and validates the 7-method interface contract. Running these examples
|
|
133
|
+
against all adapters in one CI pipeline catches interface drift immediately.
|
|
134
|
+
- `Registry.register(:gtk4, Gtk4)` at the bottom of `gtk4.rb` provides
|
|
135
|
+
automatic discovery — no configuration files, no plugin manifests.
|
|
136
|
+
- The fpm-based build can produce multiple `.deb` packages from the same
|
|
137
|
+
source tree by varying the file inclusion list.
|
|
138
|
+
- When `Base` adds a new abstract method (as happened with `announce`,
|
|
139
|
+
`accessible?`, `text_direction`), all adapter specs fail in the same CI
|
|
140
|
+
run, forcing immediate fixes.
|
|
141
|
+
|
|
142
|
+
## Consequences
|
|
143
|
+
|
|
144
|
+
Implementation is deferred to P3 (ui_framework) and P4 (packaging), but the
|
|
145
|
+
architecture is documented now:
|
|
146
|
+
|
|
147
|
+
- Future: `lib/rosett_ai/ui/gtk4.rb` + `rosett-ai-gtk4.gemspec` added to this repo
|
|
148
|
+
- Debian `Depends:` on `raictl (>= current_version)` for all GUI packages
|
|
149
|
+
- CI test matrix: core tests always run; GTK4/Qt6 tests run conditionally
|
|
150
|
+
based on toolkit availability
|
|
151
|
+
- Shared examples validate all registered adapters in a single CI run
|
|
152
|
+
- No code changes needed now — the Registry pattern already supports this
|
|
153
|
+
|
|
154
|
+
### P4 detail: variant packaging
|
|
155
|
+
|
|
156
|
+
`bin/raictl build package` will be extended with a `--variant` flag:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
bin/raictl build package --variant core # Core + TUI only (default)
|
|
160
|
+
bin/raictl build package --variant gtk4 # rosett-ai-gtk4 .deb only
|
|
161
|
+
bin/raictl build package --variant qt6 # Rosett-AI-qt6 .deb only
|
|
162
|
+
bin/raictl build package --variant all # Build all packages in one invocation
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`--variant all` iterates over all known variants and produces one `.deb`
|
|
166
|
+
per variant. This is the expected invocation for release automation.
|
|
167
|
+
|
|
168
|
+
### P3/P4 detail: GUI adapter testing
|
|
169
|
+
|
|
170
|
+
Every UI adapter — TUI and GUI — must have maximum test coverage. The TUI
|
|
171
|
+
adapter is testable with standard RSpec (no display server required). GUI
|
|
172
|
+
adapters require a different strategy because they render to a display.
|
|
173
|
+
|
|
174
|
+
GUI testing approaches to evaluate during P3 implementation:
|
|
175
|
+
|
|
176
|
+
| Approach | How it works | Toolkit support |
|
|
177
|
+
|----------|-------------|-----------------|
|
|
178
|
+
| Headless display (Xvfb) | Virtual X11 framebuffer; no physical display needed | GTK4, Qt6, KDE |
|
|
179
|
+
| ATK/AT-SPI introspection | Query widget tree via accessibility API; assert structure and state | GTK4 (native), Qt6 (via bridge) |
|
|
180
|
+
| dogtail / ldtp | Python libraries that drive GUI apps via AT-SPI; can click, type, assert | GTK4, Qt6 |
|
|
181
|
+
| GLib test harness | GTK's own `Gtk.test_*` functions for widget testing | GTK4 only |
|
|
182
|
+
| Qt Test / QTest | Qt's built-in test framework with `QTest::mouseClick`, `QTest::keyPress` | Qt6 only |
|
|
183
|
+
| Screenshot comparison | Render to headless display, capture screenshot, compare against baseline | Any toolkit |
|
|
184
|
+
|
|
185
|
+
The recommended layered approach:
|
|
186
|
+
|
|
187
|
+
1. **Unit tests** (RSpec + shared_examples): validate that each adapter
|
|
188
|
+
implements the Base interface contract. No display server needed — mock
|
|
189
|
+
the toolkit bindings. This is the primary coverage layer.
|
|
190
|
+
2. **Integration tests** (Xvfb + AT-SPI): run the adapter in a headless
|
|
191
|
+
display, query the widget tree via accessibility introspection. This
|
|
192
|
+
validates that widgets are created and accessible. Runs in CI with
|
|
193
|
+
`xvfb-run`.
|
|
194
|
+
3. **Smoke tests** (optional, P4): screenshot comparison for visual
|
|
195
|
+
regression. Fragile and toolkit-version-sensitive, so only for release
|
|
196
|
+
validation, not CI gatekeeping.
|
|
197
|
+
|
|
198
|
+
The accessibility introspection approach (layer 2) has a natural synergy
|
|
199
|
+
with ADR-003's EN 301 549 compliance requirement — if the widget tree is
|
|
200
|
+
queryable via AT-SPI, the application is accessible by construction.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# ADR-006: Multi-Engine Architecture
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Proposed (2026-02-21)
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
rosett-ai's stated goal is to be a configuration management tool for AI-assisted
|
|
10
|
+
development workflows. While the initial implementation targets Claude Code
|
|
11
|
+
exclusively, the architecture should not preclude support for other AI coding
|
|
12
|
+
engines. The intent is "one program to rule them all" — author configuration
|
|
13
|
+
once, compile to multiple engines, potentially invoke several simultaneously.
|
|
14
|
+
|
|
15
|
+
Each engine has its own directory conventions, configuration format, and
|
|
16
|
+
operational model:
|
|
17
|
+
|
|
18
|
+
| Engine | Global config | Local config | Format |
|
|
19
|
+
|--------|--------------|-------------|--------|
|
|
20
|
+
| Claude Code | `~/.claude/` | `.claude/` | JSON + Markdown rules |
|
|
21
|
+
| Cursor | `~/.cursor/` | `.cursor/` | JSON |
|
|
22
|
+
| Windsurf (Codeium) | `~/.codeium/windsurf/` | `.windsurf/` | JSON |
|
|
23
|
+
| Aider | `~/.aider.conf.yml` | `.aider.conf.yml` | YAML |
|
|
24
|
+
| Continue.dev | `~/.continue/` | `.continue/` | JSON |
|
|
25
|
+
|
|
26
|
+
ADR-002 (accepted) establishes `RosettAi::PathResolver` as the centralised path
|
|
27
|
+
resolution module. This ADR extends that concept to accommodate multiple
|
|
28
|
+
engines, and defines the compiler adapter pattern for generating
|
|
29
|
+
engine-native configuration files.
|
|
30
|
+
|
|
31
|
+
### Relationship to existing design documents
|
|
32
|
+
|
|
33
|
+
- `architecture.yml` defines the multi-target AI compilation goal
|
|
34
|
+
- `compiler.yml` covers the compilation pipeline (currently Claude-only)
|
|
35
|
+
- `claude_code_configuration.yml` covers Claude Code settings specifically
|
|
36
|
+
- ADR-002 establishes PathResolver (currently Claude-only)
|
|
37
|
+
|
|
38
|
+
### Design constraint: no ERB for machine-parsed output
|
|
39
|
+
|
|
40
|
+
Templating engines like ERB were considered for generating native config
|
|
41
|
+
files (JSON, YAML). This was rejected on security and correctness grounds:
|
|
42
|
+
|
|
43
|
+
- ERB is `Kernel#eval` under the hood, widening the attack surface beyond
|
|
44
|
+
what the security design document permits
|
|
45
|
+
- ERB-in-JSON is fragile: indentation, trailing commas, and quote escaping
|
|
46
|
+
are common sources of invalid output
|
|
47
|
+
- Programmatic hash construction (`Hash` → `JSON.generate`) guarantees
|
|
48
|
+
valid output by construction and keeps data separated from logic
|
|
49
|
+
|
|
50
|
+
The existing pattern — YAML data → Ruby hash transforms → native format —
|
|
51
|
+
is safer, testable, and sufficient for all known engine config formats.
|
|
52
|
+
|
|
53
|
+
## Options
|
|
54
|
+
|
|
55
|
+
### Option A: Engine-aware PathResolver + compiler adapters
|
|
56
|
+
|
|
57
|
+
Extend `PathResolver` with an engine registry. Each engine declares its
|
|
58
|
+
path conventions. A parallel compiler adapter pattern transforms rosett-ai's
|
|
59
|
+
canonical YAML into each engine's native format.
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
# Path resolution
|
|
63
|
+
PathResolver.for(:claude).global_dir # => ~/.claude
|
|
64
|
+
PathResolver.for(:cursor).global_dir # => ~/.cursor
|
|
65
|
+
PathResolver.for(:aider).config_file # => ~/.aider.conf.yml
|
|
66
|
+
|
|
67
|
+
# Compilation
|
|
68
|
+
EngineCompiler.for(:claude).compile(source) # => JSON + Markdown
|
|
69
|
+
EngineCompiler.for(:cursor).compile(source) # => JSON
|
|
70
|
+
EngineCompiler.for(:aider).compile(source) # => YAML
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Engine definitions live in `conf/engines/<name>.yml` and declare paths,
|
|
74
|
+
format, and compiler class. New engines are added by writing a YAML
|
|
75
|
+
definition and a compiler adapter — no changes to core code.
|
|
76
|
+
|
|
77
|
+
Pros:
|
|
78
|
+
|
|
79
|
+
- Clean separation: paths in PathResolver, transforms in compiler adapters
|
|
80
|
+
- New engines require zero changes to existing code
|
|
81
|
+
- Shared test infrastructure via shared_examples (same pattern as UI adapters)
|
|
82
|
+
- Each compiler adapter is independently testable
|
|
83
|
+
- Data/logic separation maintained (no eval surface)
|
|
84
|
+
|
|
85
|
+
Cons:
|
|
86
|
+
|
|
87
|
+
- Significant design work before implementation
|
|
88
|
+
- Engine config formats may diverge enough that a common YAML source is
|
|
89
|
+
impractical for some engines
|
|
90
|
+
- Risk of premature abstraction if only Claude Code is used for a long time
|
|
91
|
+
|
|
92
|
+
### Option B: Separate tool per engine
|
|
93
|
+
|
|
94
|
+
Create independent executables: `rosett-ai-claude`, `rosett-ai-cursor`, etc. Each
|
|
95
|
+
is self-contained with its own paths and compiler.
|
|
96
|
+
|
|
97
|
+
Pros:
|
|
98
|
+
|
|
99
|
+
- Maximum simplicity per engine
|
|
100
|
+
- No abstraction overhead
|
|
101
|
+
|
|
102
|
+
Cons:
|
|
103
|
+
|
|
104
|
+
- Duplicated logic across tools
|
|
105
|
+
- No unified workflow for multi-engine users
|
|
106
|
+
- Contradicts the "one program to rule them all" goal
|
|
107
|
+
|
|
108
|
+
### Option C: Plugin-only extensibility
|
|
109
|
+
|
|
110
|
+
Keep rosett-ai Claude-only in core. Third-party plugins add engine support.
|
|
111
|
+
|
|
112
|
+
Pros:
|
|
113
|
+
|
|
114
|
+
- Core stays simple
|
|
115
|
+
- Community can contribute engines
|
|
116
|
+
|
|
117
|
+
Cons:
|
|
118
|
+
|
|
119
|
+
- No guarantee of interface consistency
|
|
120
|
+
- Plugin API must be designed and maintained
|
|
121
|
+
- Quality control is harder across third-party code
|
|
122
|
+
|
|
123
|
+
## Decision
|
|
124
|
+
|
|
125
|
+
Deferred — this ADR captures the architectural direction established during
|
|
126
|
+
ADR-002 review. The recommended approach is **Option A**, but implementation
|
|
127
|
+
details require further analysis during the compiler design phase (P2).
|
|
128
|
+
|
|
129
|
+
Key principles agreed:
|
|
130
|
+
|
|
131
|
+
1. PathResolver must be designed with engine extensibility in mind
|
|
132
|
+
2. Compiler adapters (not templates) transform YAML to native formats
|
|
133
|
+
3. No ERB or eval-based templating for machine-parsed output
|
|
134
|
+
4. Engine definitions are declarative (YAML), not code-driven
|
|
135
|
+
5. The existing Claude Code implementation is the first adapter, not a
|
|
136
|
+
special case
|
|
137
|
+
|
|
138
|
+
## Consequences
|
|
139
|
+
|
|
140
|
+
- PathResolver (ADR-002) should use a design that can later accept an
|
|
141
|
+
engine parameter without breaking the current single-engine API
|
|
142
|
+
- The compiler design document should acknowledge multi-engine as a
|
|
143
|
+
future requirement
|
|
144
|
+
- No implementation work is needed now — Claude Code remains the only
|
|
145
|
+
target until this ADR is accepted and scheduled
|
|
146
|
+
- When implemented, `conf/engines/claude.yml` would replace the
|
|
147
|
+
hardcoded Claude Code assumptions in the current compiler
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# ADR-007: Engine-Agnostic Architecture Pivot
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted (2026-02-26) — supersedes ADR-006 (Proposed)
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
raictl was designed as a configuration management tool for AI-assisted
|
|
10
|
+
development workflows. Despite the stated goal of AI-agnosticism, the
|
|
11
|
+
implementation is Claude Code-centric: the project is named "NeatNerds
|
|
12
|
+
Claude Companion", rosett-ai lives inside `~/.claude/`, the config compiler
|
|
13
|
+
targets Claude Code's `settings.json` format, and the adopt system
|
|
14
|
+
hard-depends on the Anthropic API.
|
|
15
|
+
|
|
16
|
+
A comprehensive audit (2026-02-26) revealed:
|
|
17
|
+
|
|
18
|
+
- **2 hard couplings**: Config compilation (100% Claude Code) and Adopt
|
|
19
|
+
API analysis (Anthropic-only)
|
|
20
|
+
- **3 soft couplings**: Default output paths, CLI description strings,
|
|
21
|
+
documentation references
|
|
22
|
+
- **Existing abstraction**: The compiler already has a multi-target
|
|
23
|
+
backend pattern (ClaudeBackend, GenericBackend) via strategy pattern
|
|
24
|
+
|
|
25
|
+
A structured Q&A session produced 9 architectural decisions that resolve
|
|
26
|
+
ADR-006's deferred status and define the concrete path to AI-agnosticism.
|
|
27
|
+
|
|
28
|
+
### Relationship to existing ADRs and design documents
|
|
29
|
+
|
|
30
|
+
- **ADR-006** (Proposed): Identified multi-engine as a future need,
|
|
31
|
+
recommended Option A (engine-aware PathResolver + compiler adapters),
|
|
32
|
+
but deferred implementation. This ADR accepts and extends that
|
|
33
|
+
direction with specific decisions.
|
|
34
|
+
- **ADR-002** (Accepted): PathResolver with `engine:` parameter — already
|
|
35
|
+
designed for multi-engine, just not activated.
|
|
36
|
+
- **ADR-005** (Accepted): Package splitting strategy — applies to engine
|
|
37
|
+
packages (`rosett-ai-engine-*`) in addition to UI packages.
|
|
38
|
+
- `architecture.yml`: Must be updated to reflect engine-agnostic core.
|
|
39
|
+
- `compiler.yml`: Multi-target pipeline already designed; `--target`
|
|
40
|
+
renamed to `--engine`.
|
|
41
|
+
- `claude_code_configuration.yml`: Becomes Claude engine-specific config.
|
|
42
|
+
- `aaif_alignment.yml`: AGENTS.md becomes a first-class engine.
|
|
43
|
+
|
|
44
|
+
## Decisions
|
|
45
|
+
|
|
46
|
+
### Q1: Identity & Naming
|
|
47
|
+
|
|
48
|
+
**Decision**: Rename from "NeatNerds Claude Companion" to **"NeatNerds
|
|
49
|
+
Code Companion"**. The acronym `rosett-ai` is preserved. Binary name, package
|
|
50
|
+
names (`rosett-ai`, `rosett-ai-gtk4`, `rosett-ai-kde`), and repository name are unchanged.
|
|
51
|
+
|
|
52
|
+
Rationale: Path of least resistance. The acronym survives, all CLI
|
|
53
|
+
commands stay `rosett-ai`, `.deb` package names don't change, git history is
|
|
54
|
+
continuous.
|
|
55
|
+
|
|
56
|
+
Scope: gemspec summary/description, CLAUDE.md headers, i18n strings,
|
|
57
|
+
doc headers, CLI descriptions. SPDX copyright lines are unaffected
|
|
58
|
+
(they reference the author, not the product).
|
|
59
|
+
|
|
60
|
+
Note: GitLab project description must be updated manually by the
|
|
61
|
+
project owner.
|
|
62
|
+
|
|
63
|
+
### Q2: Generic vs Engine-Specific Layers
|
|
64
|
+
|
|
65
|
+
**Decision**: Strict separation. The generic layer captures **only**
|
|
66
|
+
human intent (behaviour rules, design constraints, coding standards).
|
|
67
|
+
Engine-specific configuration lives entirely inside each engine's own
|
|
68
|
+
config namespace (`conf/engines/<name>/`). No `extensions:` key in the
|
|
69
|
+
generic schema.
|
|
70
|
+
|
|
71
|
+
Each engine declares a **capability manifest** listing which generic
|
|
72
|
+
features it supports. At compile time, rosett-ai warns about gaps:
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
WARN behaviour/security_internal.yml: sensitive filtering not supported
|
|
76
|
+
by cursor engine — file included without redaction (review manually)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Warnings are non-fatal by default. A `--strict` flag makes them fatal
|
|
80
|
+
for CI gating.
|
|
81
|
+
|
|
82
|
+
### Q3: Engine Registry Architecture
|
|
83
|
+
|
|
84
|
+
**Decision**: Option C — **engine as namespace directory**. Each AI tool
|
|
85
|
+
is a self-contained directory under `lib/rosett_ai/engines/<name>/` containing
|
|
86
|
+
independent, optional components:
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
lib/rosett_ai/engines/claude/
|
|
90
|
+
├── backend.rb # Compiler backend
|
|
91
|
+
├── config_adapter.rb # Settings compilation
|
|
92
|
+
├── path_resolver.rb # Output paths
|
|
93
|
+
├── scaffolder.rb # Init structure
|
|
94
|
+
├── detector.rb # Autodetection
|
|
95
|
+
├── executor.rb # API/CLI invocation (for quorum)
|
|
96
|
+
├── validator.rb # Output validation
|
|
97
|
+
└── manifest.yml # Capabilities + required config + detection
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Components are optional. A trivial engine (Cursor) may only have
|
|
101
|
+
`backend.rb`, `detector.rb`, and `manifest.yml`. The engine registry
|
|
102
|
+
discovers entire directories. No forced common interface — each
|
|
103
|
+
component implements what's relevant and skips what isn't.
|
|
104
|
+
|
|
105
|
+
### Q4: Config Compilation
|
|
106
|
+
|
|
107
|
+
**Decision**: Config compilation moves inside each engine. The current
|
|
108
|
+
`RosettAi::Config` namespace (KeyMap, ScopeRouter, DomainTransformer,
|
|
109
|
+
Compiler) becomes `RosettAi::Engines::Claude::ConfigAdapter` internals.
|
|
110
|
+
|
|
111
|
+
The `--target` flag is renamed to `--engine` universally. Default engine
|
|
112
|
+
is configurable, suggested by autodetection at `bin/raictl init` time.
|
|
113
|
+
|
|
114
|
+
Additional capabilities added to engine architecture:
|
|
115
|
+
|
|
116
|
+
- **Autodetection**: Each engine can detect if its AI tool is installed
|
|
117
|
+
(binary in PATH, config directory exists, env vars set). Detection is
|
|
118
|
+
data-driven from the manifest, with optional Ruby override for custom
|
|
119
|
+
logic. CLI: `bin/raictl engines list|detect|status`.
|
|
120
|
+
- **Quorum**: Separate module (`lib/rosett_ai/quorum/`) for dispatching the
|
|
121
|
+
same analysis to multiple engines and comparing results. Initial scope:
|
|
122
|
+
adopt use case only (structured findings comparison). Not an
|
|
123
|
+
orchestrator — rosett-ai stays configuration-focused.
|
|
124
|
+
|
|
125
|
+
### Q5: Output Path Ownership
|
|
126
|
+
|
|
127
|
+
**Decision**: XDG-compliant paths. rosett-ai owns `~/.config/rosett-ai/` for its
|
|
128
|
+
own configuration. Compiled outputs deploy TO each engine's native
|
|
129
|
+
directory.
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
~/.config/rosett-ai/ # Rosett-AI's own config (XDG_CONFIG_HOME)
|
|
133
|
+
├── config.yml # default engine, quorum settings
|
|
134
|
+
├── conf/behaviour/*.yml # source YAML (human intent)
|
|
135
|
+
├── conf/design/*.yml # design documents
|
|
136
|
+
├── conf/schemas/*.json # validation schemas
|
|
137
|
+
└── conf/engines/ # per-engine config
|
|
138
|
+
├── claude/
|
|
139
|
+
├── cursor/
|
|
140
|
+
└── goose/
|
|
141
|
+
|
|
142
|
+
# Compiled outputs deploy to each engine's native location:
|
|
143
|
+
~/.claude/rules/ # Claude
|
|
144
|
+
./.cursorrules # Cursor
|
|
145
|
+
./AGENTS.md # AGENTS.md
|
|
146
|
+
./.github/copilot-instructions.md # Copilot
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Consistent with ADR-002 (PathResolver) and existing XDG research.
|
|
150
|
+
|
|
151
|
+
### Q6: Adopt System
|
|
152
|
+
|
|
153
|
+
**Decision**: Adopt defaults to local-only structural checks. API
|
|
154
|
+
analysis is opt-in via `--api` flag. The `--api` flag is quorum-capable.
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
bin/raictl adopt # Local only (default)
|
|
158
|
+
bin/raictl adopt --api # Via default engine
|
|
159
|
+
bin/raictl adopt --api --engine claude # Via specific engine
|
|
160
|
+
bin/raictl adopt --api --quorum # All invocable engines
|
|
161
|
+
bin/raictl adopt --api --quorum --engines claude,ollama # Specific set
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The `anthropic` gem moves from rosett-ai core to the Claude engine's
|
|
165
|
+
dependencies. Core rosett-ai has zero LLM API dependencies.
|
|
166
|
+
|
|
167
|
+
### Q7: Engine Scope & Phasing
|
|
168
|
+
|
|
169
|
+
**Decision**: Phased rollout, prioritising local testability:
|
|
170
|
+
|
|
171
|
+
| Phase | Engines | Rationale |
|
|
172
|
+
|-------|---------|-----------|
|
|
173
|
+
| 0 | Claude (restructure), Generic (restructure) | Prove architecture |
|
|
174
|
+
| 1 | AGENTS.md | First multi-engine story |
|
|
175
|
+
| 2 | Ollama, GPT-NeoX | Local quorum without paid APIs |
|
|
176
|
+
| 3 | Goose, Aider | CLI-invocable, richer config |
|
|
177
|
+
| 4 | Cursor, Copilot, Windsurf | Trivial formats, large user bases |
|
|
178
|
+
|
|
179
|
+
GPT-NeoX is a separate engine (not a model profile within Ollama) to
|
|
180
|
+
ensure deployment independence. Air-gapped environments may run GPT-NeoX
|
|
181
|
+
on vLLM, TGI, or custom inference without Ollama.
|
|
182
|
+
|
|
183
|
+
### Q8: Migration Path
|
|
184
|
+
|
|
185
|
+
**Decision**: Incremental migration. Version bump to **1.0.0** — the
|
|
186
|
+
engine-agnostic architecture is the v1 identity.
|
|
187
|
+
|
|
188
|
+
Engine Phase 0 splits into three sub-phases:
|
|
189
|
+
|
|
190
|
+
- **0a**: Rename "Claude Companion" → "Code Companion", bump to 1.0.0
|
|
191
|
+
- **0b**: Create `engines/` directory structure, move Claude + Generic
|
|
192
|
+
- **0c**: XDG paths, `--engine` flag, detector framework
|
|
193
|
+
|
|
194
|
+
Each sub-phase is a separate branch and merge request. Tests pass at
|
|
195
|
+
every step. Existing `~/.claude/` workflows continue working — Claude
|
|
196
|
+
engine writes to `~/.claude/rules/` as before.
|
|
197
|
+
|
|
198
|
+
### Q9: Priority vs Desktop Integration
|
|
199
|
+
|
|
200
|
+
**Decision**: Engine Phase 0 (restructure) lands first, then Desktop
|
|
201
|
+
Phase 1 (D-Bus service) starts immediately after. The two workstreams
|
|
202
|
+
are orthogonal — desktop integration is transport layer, engine
|
|
203
|
+
architecture is content layer. They can interleave after Phase 0.
|
|
204
|
+
|
|
205
|
+
## Consequences
|
|
206
|
+
|
|
207
|
+
- ADR-006 is superseded (this ADR provides the concrete decisions it
|
|
208
|
+
deferred)
|
|
209
|
+
- `architecture.yml` must be updated to reflect engine-agnostic core
|
|
210
|
+
- A new design document `engine_architecture.yml` captures the engine
|
|
211
|
+
plugin system
|
|
212
|
+
- The implementation plan defines 7 engine phases and 3 desktop phases
|
|
213
|
+
with explicit dependencies
|
|
214
|
+
- All existing tests continue passing throughout the incremental
|
|
215
|
+
migration
|
|
216
|
+
- The `anthropic` gem is removed from core runtime dependencies at
|
|
217
|
+
Phase 0b
|
|
218
|
+
- Users on current `~/.claude/`-based installations experience no
|
|
219
|
+
breakage — the Claude engine continues to write to Claude-native paths
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# ADR-008: CI Bundler Strategy and Dependency Integrity
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted (2026-03-01)
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
The CI pipeline uses `.ruby_base` as the shared template for all Ruby jobs.
|
|
10
|
+
This template runs `bundle install` in a Docker image (`ruby:3.3.10-bookworm`)
|
|
11
|
+
without specifying which Bundler groups to include or exclude, and without
|
|
12
|
+
pinning to a lockfile.
|
|
13
|
+
|
|
14
|
+
Three problems emerged:
|
|
15
|
+
|
|
16
|
+
1. **Native build failures**: The `:desktop` Bundler group contains the
|
|
17
|
+
`adwaita` gem, which transitively depends on `gobject-introspection`.
|
|
18
|
+
That gem requires `libgirepository1.0-dev` at build time. The CI Docker
|
|
19
|
+
image does not include this library, and the gem's auto-install attempt
|
|
20
|
+
fails without root privileges. No CI job needs GTK4 gems.
|
|
21
|
+
|
|
22
|
+
2. **Version drift**: Without a committed `Gemfile.lock`, CI resolves
|
|
23
|
+
dependencies fresh on each run. RuboCop `~> 1.82` resolved to 1.85.0 in
|
|
24
|
+
CI while developers ran 1.84.2 locally. With `NewCops: enable` in
|
|
25
|
+
`.rubocop.yml`, new cops in 1.85.0 produced offenses that did not exist
|
|
26
|
+
in the local version, causing false CI failures.
|
|
27
|
+
|
|
28
|
+
3. **Incomplete pre-commit linting**: The built-in overcommit `RuboCop`
|
|
29
|
+
hook only lints files in the commit diff. When a rubocop version update
|
|
30
|
+
introduces new cops, offenses in unchanged files are invisible to the
|
|
31
|
+
pre-commit hook. The CI rubocop job catches them, but the feedback loop
|
|
32
|
+
is slow and indirect.
|
|
33
|
+
|
|
34
|
+
## Decisions
|
|
35
|
+
|
|
36
|
+
### D1: Exclude the `:desktop` group in CI
|
|
37
|
+
|
|
38
|
+
The `.ruby_base` template now runs:
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
- bundle config set --local without 'desktop'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
before `bundle install`. This prevents the entire GTK4 dependency chain
|
|
45
|
+
(`adwaita -> gtk4 -> gsk4 -> gdk4 -> gdk_pixbuf2 -> gio2 ->
|
|
46
|
+
gobject-introspection`) from being resolved and built in CI.
|
|
47
|
+
|
|
48
|
+
**Rationale**: No CI job requires GTK4 gems. Desktop integration testing
|
|
49
|
+
is deferred to a future dedicated job that would install system dependencies
|
|
50
|
+
and override the `without` config.
|
|
51
|
+
|
|
52
|
+
**Impact**: All jobs extending `.ruby_base` skip `:desktop` gems. If a
|
|
53
|
+
desktop-specific CI job is ever needed, it must override the `before_script`
|
|
54
|
+
to remove the `without` exclusion and install `libgirepository1.0-dev`,
|
|
55
|
+
`libgtk-4-dev`, and related system packages.
|
|
56
|
+
|
|
57
|
+
### D2: Track `Gemfile.lock` and freeze CI installs
|
|
58
|
+
|
|
59
|
+
Two complementary changes:
|
|
60
|
+
|
|
61
|
+
1. `Gemfile.lock` is removed from `.gitignore` and committed to the
|
|
62
|
+
repository. This captures exact resolved versions for all dependencies.
|
|
63
|
+
|
|
64
|
+
2. The `.ruby_base` template now runs:
|
|
65
|
+
|
|
66
|
+
```yaml
|
|
67
|
+
- bundle config set --local frozen true
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
before `bundle install`. This makes CI fail immediately if the lockfile
|
|
71
|
+
does not match the Gemfile, preventing silent resolution to newer versions.
|
|
72
|
+
|
|
73
|
+
**Rationale**: For application-like projects (as opposed to libraries),
|
|
74
|
+
tracking the lockfile is standard practice. It ensures CI runs the same
|
|
75
|
+
versions that developers test locally. The `frozen` flag enforces this.
|
|
76
|
+
|
|
77
|
+
**Trade-off**: Developers must now run `bundle update <gem>` explicitly
|
|
78
|
+
and commit the updated lockfile when upgrading dependencies. This is
|
|
79
|
+
intentional friction that prevents accidental upgrades.
|
|
80
|
+
|
|
81
|
+
### D3: Full-project RuboCop via custom overcommit hook
|
|
82
|
+
|
|
83
|
+
The built-in overcommit `RuboCop` hook is disabled. A custom `RubocopAll`
|
|
84
|
+
hook replaces it:
|
|
85
|
+
|
|
86
|
+
- **File**: `.git-hooks/pre_commit/rubocop_all.rb`
|
|
87
|
+
- **Behaviour**: Runs `bundle exec rubocop` against all project files, not
|
|
88
|
+
just changed files.
|
|
89
|
+
- **Config**: Same flags as the original (`--config .rubocop.yml
|
|
90
|
+
--force-exclusion --display-cop-names`).
|
|
91
|
+
|
|
92
|
+
**Rationale**: The per-file hook misses offenses in unchanged files. After
|
|
93
|
+
a rubocop upgrade introduces new cops, the pre-commit hook must catch all
|
|
94
|
+
offenses before they reach CI. Running rubocop on all ~245 files takes
|
|
95
|
+
under 10 seconds, so the performance cost is acceptable.
|
|
96
|
+
|
|
97
|
+
**Trade-off**: Every commit triggers a full rubocop scan regardless of
|
|
98
|
+
what changed. For the current codebase size this is negligible, but may
|
|
99
|
+
need revisiting if the project grows significantly.
|
|
100
|
+
|
|
101
|
+
## Consequences
|
|
102
|
+
|
|
103
|
+
### Positive
|
|
104
|
+
|
|
105
|
+
- CI `bundle install` no longer attempts to build native GTK4 extensions.
|
|
106
|
+
- All validate, code quality, security, test, and build jobs can run
|
|
107
|
+
without system-level GTK4 development libraries.
|
|
108
|
+
- CI uses identical gem versions to development, eliminating false
|
|
109
|
+
positives from version drift.
|
|
110
|
+
- Pre-commit hooks catch all rubocop offenses before code reaches CI.
|
|
111
|
+
|
|
112
|
+
### Negative
|
|
113
|
+
|
|
114
|
+
- Developers must explicitly update the lockfile: `bundle update <gem>`
|
|
115
|
+
followed by committing `Gemfile.lock`.
|
|
116
|
+
- If a developer forgets to commit the lockfile after a Gemfile change,
|
|
117
|
+
CI will fail with a frozen bundle error. This is a feature, not a bug.
|
|
118
|
+
- Pre-commit rubocop runs take ~10 seconds per commit instead of ~1 second
|
|
119
|
+
(full scan vs changed files only).
|
|
120
|
+
|
|
121
|
+
## Files
|
|
122
|
+
|
|
123
|
+
| File | Change |
|
|
124
|
+
|------|--------|
|
|
125
|
+
| `.gitlab-ci-files/global/defaults.yml` | Added `without 'desktop'` and `frozen true` to `.ruby_base` |
|
|
126
|
+
| `.gitignore` | Removed `Gemfile.lock` entry |
|
|
127
|
+
| `Gemfile.lock` | Now tracked |
|
|
128
|
+
| `.overcommit.yml` | Disabled `RuboCop`, added `RubocopAll` |
|
|
129
|
+
| `.git-hooks/pre_commit/rubocop_all.rb` | Custom overcommit hook |
|