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,475 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gui_plugins
|
|
3
|
+
domain: ui
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
status: implemented
|
|
6
|
+
priority: 3
|
|
7
|
+
author: hugo
|
|
8
|
+
created_at: "2026-03-10"
|
|
9
|
+
modified_at: "2026-03-16"
|
|
10
|
+
modified_by: claude
|
|
11
|
+
depends_on:
|
|
12
|
+
- architecture
|
|
13
|
+
- engine_architecture
|
|
14
|
+
- desktop_integration
|
|
15
|
+
- security
|
|
16
|
+
- distribution
|
|
17
|
+
- error_handling
|
|
18
|
+
|
|
19
|
+
intent: |
|
|
20
|
+
Define the pluggable GUI architecture that treats desktop frontends as
|
|
21
|
+
first-class plugins — the same pattern used for engine plugins. GUI logic
|
|
22
|
+
must not live inside the rosett-ai core gem. Each desktop toolkit (GTK4, Qt6,
|
|
23
|
+
future web) is packaged as a separate gem (rosett-ai-gui-<toolkit>) that
|
|
24
|
+
self-registers via the plugin registry and implements a GuiContract.
|
|
25
|
+
|
|
26
|
+
GUIs are plugin managers. Every GUI must provide full lifecycle management
|
|
27
|
+
for all Rosett-AI plugin types: engines, MCP servers, and content packs. Users
|
|
28
|
+
install, remove, configure, and update plugins through the GUI. Under the
|
|
29
|
+
hood, plugin operations flow through D-Bus to Rosett-AI core, which delegates
|
|
30
|
+
to a PackageManager abstraction (apt, gem, future: dnf, brew, winget).
|
|
31
|
+
|
|
32
|
+
The D-Bus service and client remain in Rosett-AI core. The service enables
|
|
33
|
+
headless automation (CI, scripting, tiling WM integration) without any
|
|
34
|
+
GUI installed. GUI gems are D-Bus clients — they communicate exclusively
|
|
35
|
+
via D-Bus and never import core Ruby modules directly. This ensures that
|
|
36
|
+
any toolkit can be used without coupling to Ruby internals.
|
|
37
|
+
|
|
38
|
+
GUI gems ship all freedesktop.org XDG artifacts: .desktop files, scalable
|
|
39
|
+
icons, AppStream metainfo, and D-Bus activation service files. These are
|
|
40
|
+
installed to standard XDG paths by the package manager (apt) or by the
|
|
41
|
+
gem's post-install hook. All artifacts use the reverse-DNS identifier
|
|
42
|
+
be.neatnerds.rosettai (not be.neatnerds.rosettai — corrected to match the
|
|
43
|
+
actual domain neatnerds.be).
|
|
44
|
+
|
|
45
|
+
GUI gems are version-locked to Rosett-AI core via gemspec dependency:
|
|
46
|
+
spec.add_dependency 'rosett-ai', "= #{VERSION}". This guarantees D-Bus API
|
|
47
|
+
compatibility between the GUI client and the core service.
|
|
48
|
+
|
|
49
|
+
Package manager abstraction provides a uniform interface for installing
|
|
50
|
+
and removing system packages (apt-get), Ruby gems, and future package
|
|
51
|
+
managers (dnf, brew, winget). System package operations require PolicyKit
|
|
52
|
+
(pkexec) for privilege escalation — the GUI never runs as root.
|
|
53
|
+
|
|
54
|
+
Implementation phases:
|
|
55
|
+
|
|
56
|
+
Phase 1 — GuiContract + PackageManager abstraction in core:
|
|
57
|
+
lib/rosett_ai/plugins/gui_contract.rb (contract module)
|
|
58
|
+
lib/rosett_ai/package_manager/ (base, apt, gem backends)
|
|
59
|
+
D-Bus interface extensions for plugin management
|
|
60
|
+
Reverse-DNS rename: be.neatnerds.rosettai → be.neatnerds.rosettai
|
|
61
|
+
|
|
62
|
+
Phase 2 — Extract GTK4 code to rosett-ai-gui-gtk4 gem:
|
|
63
|
+
Move lib/rosett_ai/desktop/gtk4_app.rb → lib/rosett_ai_gui/gtk4/app.rb
|
|
64
|
+
Move lib/rosett_ai/desktop/gtk4_preferences.rb → lib/rosett_ai_gui/gtk4/preferences.rb
|
|
65
|
+
Move lib/rosett_ai/desktop/gui_logger.rb → lib/rosett_ai_gui/gtk4/gui_logger.rb
|
|
66
|
+
Add plugin management views (plugin_manager.rb)
|
|
67
|
+
Self-registration via rosett_ai_gui/gtk4/register.rb
|
|
68
|
+
XDG artifacts in share/ directory
|
|
69
|
+
|
|
70
|
+
Phase 3 — CI, packaging, validation:
|
|
71
|
+
GitLab CI pipeline for Rosett-AI-gui-gtk4 gem
|
|
72
|
+
Debian package build (rosett-ai-gui-gtk4)
|
|
73
|
+
AppStream and desktop-file validation in CI
|
|
74
|
+
Integration tests with mock D-Bus service
|
|
75
|
+
|
|
76
|
+
This design governs pluggable GUI gem architecture, plugin lifecycle
|
|
77
|
+
management, and PackageManager abstraction. The D-Bus service, compositor
|
|
78
|
+
adapters, and desktop service architecture are governed by
|
|
79
|
+
desktop_integration.yml. Engine plugin architecture is governed by
|
|
80
|
+
engine_architecture.yml. Error handling follows error_handling.yml.
|
|
81
|
+
|
|
82
|
+
constraints:
|
|
83
|
+
- "No desktop GUI logic in Rosett-AI core gem — all GUI code lives in rosett-ai-gui-<toolkit> gems"
|
|
84
|
+
- "GUI plugins are separate gems following the pattern rosett-ai-gui-<toolkit>"
|
|
85
|
+
- "GUI gems are version-locked to core via gemspec — spec.add_dependency 'rosett-ai', '= VERSION'"
|
|
86
|
+
- "D-Bus service and D-Bus client library remain in Rosett-AI core"
|
|
87
|
+
- "GUI communicates exclusively via D-Bus — no direct Ruby API imports from core"
|
|
88
|
+
- "All XDG artifacts (.desktop, icons, metainfo, D-Bus service) ship inside the GUI gem or package"
|
|
89
|
+
- "Reverse-DNS identifier is be.neatnerds.rosettai everywhere — bus name, object paths, file names"
|
|
90
|
+
- "Package manager operations for system packages require PolicyKit (pkexec) for privilege escalation"
|
|
91
|
+
- "GUI gems self-register via Gem.find_files('rosett_ai_gui/*/register.rb') — same pattern as engine plugins"
|
|
92
|
+
- "Plugin management in GUI must support all plugin types — engines, MCP servers, content packs"
|
|
93
|
+
- "Security constraints from security.yml apply to all GUI plugin code and D-Bus interactions"
|
|
94
|
+
- "AppStream metainfo and .desktop files must pass their respective validators"
|
|
95
|
+
- "This design governs pluggable GUI gem architecture and plugin lifecycle.
|
|
96
|
+
D-Bus service and compositor adapters are governed by desktop_integration.yml"
|
|
97
|
+
|
|
98
|
+
acceptance_criteria:
|
|
99
|
+
- "GuiContract module exists in lib/rosett_ai/plugins/gui_contract.rb"
|
|
100
|
+
- "GUI gem self-registers via Gem.find_files('rosett_ai_gui/*/register.rb')"
|
|
101
|
+
- "bin/raictl plugins list shows GUI plugins with type :gui alongside engine plugins"
|
|
102
|
+
- ".desktop file installed to share/applications/be.neatnerds.rosettai.desktop passes desktop-file-validate"
|
|
103
|
+
- "Scalable icon installed to share/icons/hicolor/scalable/apps/be.neatnerds.rosettai.svg"
|
|
104
|
+
- "AppStream metainfo at share/metainfo/be.neatnerds.rosettai.metainfo.xml passes appstreamcli validate"
|
|
105
|
+
- "GUI can list installed plugins of all types via D-Bus PluginManager interface"
|
|
106
|
+
- "GUI can install a plugin via D-Bus → PackageManager → apt-get or gem install"
|
|
107
|
+
- "GUI can remove a plugin via D-Bus → PackageManager → apt-get or gem uninstall"
|
|
108
|
+
- "GUI can open a configuration view for any installed plugin"
|
|
109
|
+
- "PackageManager::Apt backend wraps apt-get with pkexec for privilege escalation"
|
|
110
|
+
- "PackageManager::Gem backend wraps gem install/uninstall without privilege escalation"
|
|
111
|
+
- "All occurrences of be.neatnerds.rosettai renamed to be.neatnerds.rosettai in core and specs"
|
|
112
|
+
- "D-Bus service starts on be.neatnerds.rosettai bus name"
|
|
113
|
+
- "rosett-ai-gui-gtk4 gem installs and launches without Rosett-AI core needing any GUI dependencies"
|
|
114
|
+
- "Exit code 0 on success, 1 on plugin operation failure, 5 on missing GUI dependency"
|
|
115
|
+
- "TTY-aware output: formatted table for plugins list when interactive, JSON when piped"
|
|
116
|
+
|
|
117
|
+
examples:
|
|
118
|
+
- scenario: "User installs rosett-ai-gui-gtk4 Debian package"
|
|
119
|
+
expected: |
|
|
120
|
+
Package installs the gem, .desktop file, icon, and metainfo to standard
|
|
121
|
+
XDG paths. GTK4 app appears in GNOME application menu. Launching it
|
|
122
|
+
connects to be.neatnerds.rosettai D-Bus service (auto-activates if needed).
|
|
123
|
+
not: "GUI code is bundled inside Rosett-AI core. .desktop file uses be.neatnerds.rosettai identifier."
|
|
124
|
+
- scenario: "User opens GUI and manages engine plugins"
|
|
125
|
+
expected: |
|
|
126
|
+
Plugin manager view lists installed engines (claude, generic, agents_md).
|
|
127
|
+
User clicks install, selects rosett-ai-engine-ollama from available packages.
|
|
128
|
+
GUI calls D-Bus PluginManager.Install('engine', 'ollama'). Core delegates
|
|
129
|
+
to PackageManager::Apt which runs pkexec apt-get install rosett-ai-engine-ollama.
|
|
130
|
+
After install, new engine appears in the list.
|
|
131
|
+
not: "GUI calls bin/raictl CLI directly. Installation runs without privilege escalation."
|
|
132
|
+
- scenario: "Developer creates a new rosett-ai-gui-web gem"
|
|
133
|
+
expected: |
|
|
134
|
+
Developer implements GuiContract in lib/rosett_ai_gui/web/gui.rb, adds
|
|
135
|
+
register.rb for self-registration. Gem is discoverable by core via
|
|
136
|
+
Gem.find_files. bin/raictl plugins list shows the web GUI alongside gtk4.
|
|
137
|
+
Web GUI communicates with core via D-Bus WebSocket bridge.
|
|
138
|
+
not: "Web GUI requires changes to Rosett-AI core. Developer must modify plugin registry manually."
|
|
139
|
+
- scenario: "User on Fedora installs rosett-ai-gui-gtk4 RPM"
|
|
140
|
+
expected: |
|
|
141
|
+
PackageManager::Dnf backend (future) handles installation. Same D-Bus
|
|
142
|
+
communication, same plugin management UI. Only the package manager
|
|
143
|
+
backend differs. Falls back to PackageManager::Gem if no system
|
|
144
|
+
package manager is configured.
|
|
145
|
+
not: "Installation fails because apt is hardcoded. GUI assumes Debian-based system."
|
|
146
|
+
|
|
147
|
+
anti_patterns:
|
|
148
|
+
- "GUI code in Rosett-AI core gem (all GUI logic belongs in rosett-ai-gui-<toolkit> gems)"
|
|
149
|
+
- "GUI importing core Ruby modules directly (must communicate via D-Bus only)"
|
|
150
|
+
- "Hardcoding apt as the only package manager (use PackageManager abstraction)"
|
|
151
|
+
- "Using be.neatnerds.rosettai instead of be.neatnerds.rosettai (domain is neatnerds.be)"
|
|
152
|
+
- "Plugin management bypassing D-Bus (no direct CLI calls from GUI process)"
|
|
153
|
+
- "GUI running package operations as root (must use pkexec for privilege escalation)"
|
|
154
|
+
- "Tight coupling between GUI gem and a specific package manager backend"
|
|
155
|
+
- "Shipping XDG artifacts from Rosett-AI core instead of from the GUI gem"
|
|
156
|
+
- "Version mismatch between GUI gem and core (must be exact version lock)"
|
|
157
|
+
|
|
158
|
+
gui_notes: |
|
|
159
|
+
Document interactions (cross-references):
|
|
160
|
+
|
|
161
|
+
1. desktop_integration.yml: gui_plugins governs pluggable GUI gem
|
|
162
|
+
architecture and plugin lifecycle. desktop_integration governs D-Bus
|
|
163
|
+
service, compositor adapters, and desktop service architecture.
|
|
164
|
+
|
|
165
|
+
2. engine_architecture.yml: engine plugins and GUI plugins share the
|
|
166
|
+
same self-registration pattern (Gem.find_files). GUI plugin management
|
|
167
|
+
covers engine install/remove/configure via D-Bus.
|
|
168
|
+
|
|
169
|
+
3. distribution.yml: GUI gems are distributed as Debian packages
|
|
170
|
+
alongside Rosett-AI core. Same Pulp repository, same CI pipeline pattern.
|
|
171
|
+
|
|
172
|
+
4. security.yml: GUI D-Bus interactions follow security constraints.
|
|
173
|
+
PackageManager uses array-form system() for subprocess spawning.
|
|
174
|
+
|
|
175
|
+
5. error_handling.yml: plugin operation errors follow the structured
|
|
176
|
+
error hierarchy. Exit codes for plugins list/install/remove commands.
|
|
177
|
+
|
|
178
|
+
6. content_packs.yml: GUI plugin management covers content pack
|
|
179
|
+
install/remove alongside engines and MCP servers.
|
|
180
|
+
|
|
181
|
+
GuiContract Definition:
|
|
182
|
+
|
|
183
|
+
module RosettAi::Plugins::GuiContract
|
|
184
|
+
include RosettAi::Plugins::Contract
|
|
185
|
+
|
|
186
|
+
def plugin_type = :gui
|
|
187
|
+
|
|
188
|
+
# Identity
|
|
189
|
+
def gui_name # 'gtk4', 'qt6', 'web'
|
|
190
|
+
def application_id # 'be.neatnerds.rosettai'
|
|
191
|
+
def toolkit # 'gtk4', 'qt6', 'web'
|
|
192
|
+
|
|
193
|
+
# Lifecycle
|
|
194
|
+
def available? # true if system dependencies are met
|
|
195
|
+
def launch(argv) # Start the GUI application
|
|
196
|
+
def required_system_packages # ['libgtk-4-1', 'libadwaita-1-0']
|
|
197
|
+
def manifest_path # Path to conf/manifest.yml in the gem
|
|
198
|
+
|
|
199
|
+
# Plugin management (required — all GUIs must support this)
|
|
200
|
+
def plugin_list_view # Show installed plugins of all types
|
|
201
|
+
def plugin_install_dialog(type) # Install dialog for :engine / :mcp / :content
|
|
202
|
+
def plugin_remove_dialog(type, name) # Removal confirmation for a plugin
|
|
203
|
+
def plugin_configure_view(type, name) # Configuration view for a plugin
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
Package Manager Abstraction:
|
|
207
|
+
|
|
208
|
+
module RosettAi::PackageManager
|
|
209
|
+
class Base
|
|
210
|
+
def install(package_name) # Install a package
|
|
211
|
+
def remove(package_name) # Remove a package
|
|
212
|
+
def installed?(package_name) # Check if a package is installed
|
|
213
|
+
def available_packages(pattern) # Search available packages
|
|
214
|
+
def update_index # Refresh package list / cache
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
class Apt < Base
|
|
218
|
+
# Wraps apt-get via pkexec for privilege escalation
|
|
219
|
+
# Uses array-form system() — never string interpolation
|
|
220
|
+
# Parses dpkg-query for installed? checks
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
class Gem < Base
|
|
224
|
+
# Wraps Gem::Installer / Gem::Uninstaller
|
|
225
|
+
# No privilege escalation needed (user gem path)
|
|
226
|
+
# Fallback when no system package manager is available
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Future backends:
|
|
230
|
+
# class Dnf < Base — Fedora/RHEL (dnf install)
|
|
231
|
+
# class Brew < Base — macOS (brew install)
|
|
232
|
+
# class Winget < Base — Windows (winget install)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
D-Bus PluginManager Interface (new, added to core service):
|
|
236
|
+
|
|
237
|
+
be.neatnerds.rosettai.PluginManager:
|
|
238
|
+
Methods:
|
|
239
|
+
ListPlugins(s type) -> a(sssa{sv})
|
|
240
|
+
# type: 'engine', 'gui', 'mcp', 'content'
|
|
241
|
+
# Returns array of (name, version, status, metadata)
|
|
242
|
+
InstallPlugin(s type, s name) -> (bs)
|
|
243
|
+
# Returns (success, message)
|
|
244
|
+
RemovePlugin(s type, s name) -> (bs)
|
|
245
|
+
# Returns (success, message)
|
|
246
|
+
GetPluginConfig(s type, s name) -> a{sv}
|
|
247
|
+
# Returns plugin configuration as dict
|
|
248
|
+
SetPluginConfig(s type, s name, a{sv} config) -> (bs)
|
|
249
|
+
# Updates plugin configuration
|
|
250
|
+
Signals:
|
|
251
|
+
PluginInstalled(s type, s name, s version)
|
|
252
|
+
PluginRemoved(s type, s name)
|
|
253
|
+
PluginConfigChanged(s type, s name)
|
|
254
|
+
|
|
255
|
+
GUI Gem Directory Structure:
|
|
256
|
+
|
|
257
|
+
rosett-ai-gui-gtk4/
|
|
258
|
+
├── lib/rosett_ai_gui/gtk4/
|
|
259
|
+
│ ├── register.rb # Self-registration with plugin registry
|
|
260
|
+
│ ├── gui.rb # GuiContract implementation
|
|
261
|
+
│ ├── app.rb # Gtk4App (extracted from core desktop/)
|
|
262
|
+
│ ├── preferences.rb # Gtk4Preferences (extracted from core)
|
|
263
|
+
│ ├── gui_logger.rb # GuiLogger (extracted from core)
|
|
264
|
+
│ ├── plugin_manager.rb # Plugin management views (new)
|
|
265
|
+
│ └── version.rb # Version constant (locked to core)
|
|
266
|
+
├── conf/manifest.yml # GUI capability manifest
|
|
267
|
+
├── share/
|
|
268
|
+
│ ├── applications/
|
|
269
|
+
│ │ └── be.neatnerds.rosettai.desktop
|
|
270
|
+
│ ├── icons/hicolor/scalable/apps/
|
|
271
|
+
│ │ └── be.neatnerds.rosettai.svg
|
|
272
|
+
│ ├── metainfo/
|
|
273
|
+
│ │ └── be.neatnerds.rosettai.metainfo.xml
|
|
274
|
+
│ └── dbus-1/services/
|
|
275
|
+
│ └── be.neatnerds.rosettai.Gui.service
|
|
276
|
+
├── packaging/scripts/
|
|
277
|
+
│ ├── postinst # Install XDG artifacts, update caches
|
|
278
|
+
│ ├── prerm # Pre-removal cleanup
|
|
279
|
+
│ └── postrm # Post-removal cache updates
|
|
280
|
+
├── spec/
|
|
281
|
+
│ ├── rosett_ai_gui/gtk4/
|
|
282
|
+
│ │ ├── gui_spec.rb
|
|
283
|
+
│ │ ├── app_spec.rb
|
|
284
|
+
│ │ ├── plugin_manager_spec.rb
|
|
285
|
+
│ │ └── register_spec.rb
|
|
286
|
+
│ └── support/
|
|
287
|
+
│ └── dbus_mock.rb # Mock D-Bus service for tests
|
|
288
|
+
├── rosett-ai-gui-gtk4.gemspec
|
|
289
|
+
├── Gemfile
|
|
290
|
+
├── .rubocop.yml
|
|
291
|
+
└── .reek.yml
|
|
292
|
+
|
|
293
|
+
GUI Manifest (conf/manifest.yml in rosett-ai-gui-gtk4):
|
|
294
|
+
|
|
295
|
+
name: gtk4
|
|
296
|
+
display_name: GTK4 Desktop App
|
|
297
|
+
plugin_type: gui
|
|
298
|
+
toolkit: gtk4
|
|
299
|
+
application_id: be.neatnerds.rosettai
|
|
300
|
+
required_system_packages:
|
|
301
|
+
- libgtk-4-1
|
|
302
|
+
- libadwaita-1-0
|
|
303
|
+
- gir1.2-gtk-4.0
|
|
304
|
+
- gir1.2-adw-1
|
|
305
|
+
capabilities:
|
|
306
|
+
plugin_management: true
|
|
307
|
+
preferences_dialog: true
|
|
308
|
+
system_tray: false # SNI is in core D-Bus service
|
|
309
|
+
focus_monitoring: false # Focus monitor is in core D-Bus service
|
|
310
|
+
xdg_artifacts:
|
|
311
|
+
desktop_file: share/applications/be.neatnerds.rosettai.desktop
|
|
312
|
+
icon_scalable: share/icons/hicolor/scalable/apps/be.neatnerds.rosettai.svg
|
|
313
|
+
metainfo: share/metainfo/be.neatnerds.rosettai.metainfo.xml
|
|
314
|
+
|
|
315
|
+
Reverse-DNS Rename Scope (be.neatnerds.rosettai → be.neatnerds.rosettai):
|
|
316
|
+
|
|
317
|
+
Core source files:
|
|
318
|
+
lib/rosett_ai/dbus/service.rb — BUS_NAME constant
|
|
319
|
+
lib/rosett_ai/dbus/manager_interface.rb — interface name string
|
|
320
|
+
lib/rosett_ai/dbus/focus_monitor_interface.rb — interface name string
|
|
321
|
+
lib/rosett_ai/dbus/status_notifier_interface.rb — interface name string
|
|
322
|
+
lib/rosett_ai/desktop/gtk4_app.rb — APPLICATION_ID constant
|
|
323
|
+
lib/rosett_ai/desktop/dbus_client.rb — bus name, object path
|
|
324
|
+
lib/rosett_ai/thor/tasks/dbus.rb — bus name references
|
|
325
|
+
|
|
326
|
+
XDG artifact files (rename file + update contents):
|
|
327
|
+
share/applications/be.neatnerds.rosettai.desktop
|
|
328
|
+
→ share/applications/be.neatnerds.rosettai.desktop
|
|
329
|
+
share/dbus-1/services/be.neatnerds.rosettai.service
|
|
330
|
+
→ share/dbus-1/services/be.neatnerds.rosettai.service
|
|
331
|
+
share/dbus-1/interfaces/be.neatnerds.rosettai.xml
|
|
332
|
+
→ share/dbus-1/interfaces/be.neatnerds.rosettai.xml
|
|
333
|
+
|
|
334
|
+
Spec files:
|
|
335
|
+
spec/rosett_ai/dbus/service_spec.rb
|
|
336
|
+
spec/rosett_ai/dbus/manager_interface_spec.rb
|
|
337
|
+
spec/rosett_ai/dbus/focus_monitor_interface_spec.rb
|
|
338
|
+
spec/rosett_ai/dbus/status_notifier_interface_spec.rb
|
|
339
|
+
spec/rosett_ai/desktop/dbus_client_spec.rb
|
|
340
|
+
spec/rosett_ai/desktop/gtk4_app_spec.rb
|
|
341
|
+
spec/rosett_ai/thor/tasks/dbus_spec.rb
|
|
342
|
+
|
|
343
|
+
Documentation:
|
|
344
|
+
CLAUDE.md, README.md, CHANGELOG.md, DESKTOP.md
|
|
345
|
+
doc/man/rai.1.ronn
|
|
346
|
+
doc/IMPLEMENTATION_PLAN.md, doc/ARCHITECTURE.md
|
|
347
|
+
conf/design/desktop_integration.yml
|
|
348
|
+
|
|
349
|
+
Self-Registration (lib/rosett_ai_gui/gtk4/register.rb):
|
|
350
|
+
|
|
351
|
+
module RosettAiGui
|
|
352
|
+
module Gtk4
|
|
353
|
+
class Register
|
|
354
|
+
def self.call(registry)
|
|
355
|
+
registry.register(
|
|
356
|
+
name: 'gtk4',
|
|
357
|
+
type: :gui,
|
|
358
|
+
klass: -> { require 'rosett_ai_gui/gtk4/gui'; RosettAiGui::Gtk4::Gui },
|
|
359
|
+
manifest: File.expand_path('../../conf/manifest.yml', __dir__)
|
|
360
|
+
)
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
Gemspec Pattern (rosett-ai-gui-gtk4.gemspec):
|
|
367
|
+
|
|
368
|
+
Gem::Specification.new do |spec|
|
|
369
|
+
spec.name = 'rosett-ai-gui-gtk4'
|
|
370
|
+
spec.version = RosettAiGui::Gtk4::VERSION
|
|
371
|
+
spec.summary = 'GTK4 desktop interface for Rosett-AI'
|
|
372
|
+
spec.license = 'GPL-3.0-only'
|
|
373
|
+
spec.authors = ['Hugo Antonio Sepulveda Manriquez']
|
|
374
|
+
spec.email = ['hugo@neatnerds.be']
|
|
375
|
+
|
|
376
|
+
spec.add_dependency 'rosett-ai', "= #{RosettAiGui::Gtk4::VERSION}"
|
|
377
|
+
spec.add_dependency 'gtk4', '~> 4.2'
|
|
378
|
+
spec.add_dependency 'adwaita', '~> 0.5'
|
|
379
|
+
|
|
380
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
interactions:
|
|
384
|
+
- action: "List installed plugins"
|
|
385
|
+
tui: "bin/raictl plugins list"
|
|
386
|
+
gtk4: "Plugin Manager view in main window"
|
|
387
|
+
qt6: "KCM plugin management panel"
|
|
388
|
+
- action: "Install a new engine"
|
|
389
|
+
tui: "bin/raictl plugins install engine ollama"
|
|
390
|
+
gtk4: "Plugin Manager → Install → select engine from list"
|
|
391
|
+
qt6: "KCM plugin panel → Install button → engine list"
|
|
392
|
+
- action: "Remove a plugin"
|
|
393
|
+
tui: "bin/raictl plugins remove engine ollama"
|
|
394
|
+
gtk4: "Plugin Manager → select plugin → Remove button → confirmation dialog"
|
|
395
|
+
qt6: "KCM plugin panel → select plugin → Remove → confirmation"
|
|
396
|
+
- action: "Configure a plugin"
|
|
397
|
+
tui: "bin/raictl plugins configure engine claude"
|
|
398
|
+
gtk4: "Plugin Manager → select plugin → Configure button → settings view"
|
|
399
|
+
qt6: "KCM plugin panel → select plugin → Configure → settings form"
|
|
400
|
+
- action: "Launch GUI application"
|
|
401
|
+
tui: "bin/raictl gui launch gtk4"
|
|
402
|
+
gtk4: "Desktop menu entry or bin/rai-gtk4"
|
|
403
|
+
qt6: "System Settings → rosett-ai module or bin/rai-qt6"
|
|
404
|
+
|
|
405
|
+
accessibility:
|
|
406
|
+
roles:
|
|
407
|
+
- element: "plugin_list"
|
|
408
|
+
role: "listbox"
|
|
409
|
+
label: "Installed plugins"
|
|
410
|
+
description: "List of all installed rai plugins grouped by type"
|
|
411
|
+
- element: "plugin_install_button"
|
|
412
|
+
role: "button"
|
|
413
|
+
label: "Install plugin"
|
|
414
|
+
description: "Opens the plugin installation dialog"
|
|
415
|
+
- element: "plugin_remove_button"
|
|
416
|
+
role: "button"
|
|
417
|
+
label: "Remove plugin"
|
|
418
|
+
description: "Removes the selected plugin after confirmation"
|
|
419
|
+
- element: "plugin_configure_button"
|
|
420
|
+
role: "button"
|
|
421
|
+
label: "Configure plugin"
|
|
422
|
+
description: "Opens configuration view for the selected plugin"
|
|
423
|
+
- element: "package_manager_status"
|
|
424
|
+
role: "status"
|
|
425
|
+
label: "Package manager status"
|
|
426
|
+
description: "Shows which package manager backend is active and its state"
|
|
427
|
+
keyboard:
|
|
428
|
+
- key: "Tab"
|
|
429
|
+
action: "Navigate between plugin list and action buttons"
|
|
430
|
+
- key: "Arrow keys"
|
|
431
|
+
action: "Navigate plugin list items"
|
|
432
|
+
- key: "Enter"
|
|
433
|
+
action: "Activate selected button or open plugin details"
|
|
434
|
+
- key: "Delete"
|
|
435
|
+
action: "Remove selected plugin (with confirmation)"
|
|
436
|
+
- key: "Escape"
|
|
437
|
+
action: "Close dialog or cancel operation"
|
|
438
|
+
announcements:
|
|
439
|
+
- event: "plugin_installed"
|
|
440
|
+
announce: "Plugin <name> installed successfully"
|
|
441
|
+
priority: "polite"
|
|
442
|
+
- event: "plugin_removed"
|
|
443
|
+
announce: "Plugin <name> removed"
|
|
444
|
+
priority: "polite"
|
|
445
|
+
- event: "plugin_install_failed"
|
|
446
|
+
announce: "Failed to install plugin <name>: <error>"
|
|
447
|
+
priority: "assertive"
|
|
448
|
+
- event: "privilege_escalation"
|
|
449
|
+
announce: "Authentication required to install system package"
|
|
450
|
+
priority: "assertive"
|
|
451
|
+
target_sizes:
|
|
452
|
+
minimum: "24x24dp"
|
|
453
|
+
recommended: "44x44dp"
|
|
454
|
+
timing:
|
|
455
|
+
no_time_limits: true
|
|
456
|
+
animations_respect_prefers_reduced_motion: true
|
|
457
|
+
|
|
458
|
+
preferences:
|
|
459
|
+
language: ruby
|
|
460
|
+
patterns:
|
|
461
|
+
- "GUI as plugin (self-registering gem, GuiContract)"
|
|
462
|
+
- "PackageManager abstraction (Apt, Gem, future: Dnf, Brew)"
|
|
463
|
+
- "D-Bus exclusive IPC (no direct Ruby API imports)"
|
|
464
|
+
- "Reverse-DNS naming (be.neatnerds.rosettai)"
|
|
465
|
+
- "XDG compliance for desktop artifacts"
|
|
466
|
+
- "Version-locked gems (exact dependency on core)"
|
|
467
|
+
- "Privilege escalation via PolicyKit (pkexec)"
|
|
468
|
+
testing: rspec with mock D-Bus service, GuiContract compliance tests,
|
|
469
|
+
PackageManager backend scenarios, plugin install/remove lifecycle,
|
|
470
|
+
XDG artifact validation, version lock enforcement, and privilege
|
|
471
|
+
escalation handling
|
|
472
|
+
gems:
|
|
473
|
+
- ruby-dbus (core, for D-Bus client)
|
|
474
|
+
- gtk4 (rosett-ai-gui-gtk4 only)
|
|
475
|
+
- adwaita (rosett-ai-gui-gtk4 only)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: i18n
|
|
3
|
+
domain: i18n
|
|
4
|
+
version: 1.1.0
|
|
5
|
+
status: implemented
|
|
6
|
+
priority: 3
|
|
7
|
+
author: hugo
|
|
8
|
+
created_at: "2026-02-18"
|
|
9
|
+
modified_at: "2026-03-17"
|
|
10
|
+
modified_by: claude
|
|
11
|
+
depends_on:
|
|
12
|
+
- architecture
|
|
13
|
+
- compiler
|
|
14
|
+
|
|
15
|
+
intent: |
|
|
16
|
+
Enable rosett-ai to support any language in the world across all surfaces: UI
|
|
17
|
+
strings (TUI + GUI), CLI output, documentation, and validation messages.
|
|
18
|
+
The approach uses ruby-i18n YAML as the single source of truth, compiled
|
|
19
|
+
to platform-native formats for each GUI toolkit (gettext for GTK4/KDE,
|
|
20
|
+
Qt Linguist for Qt6). This follows the 'author once, compile down'
|
|
21
|
+
principle already established for behaviour and design compilation.
|
|
22
|
+
|
|
23
|
+
constraints:
|
|
24
|
+
- All user-facing strings must be externalized to locale files (never hardcoded)
|
|
25
|
+
- ruby-i18n YAML format is the single source of truth for all translations
|
|
26
|
+
- Locale files must be compiled to platform-native formats (gettext .po/.mo, Qt .ts/.qm)
|
|
27
|
+
- CLDR plural rules must be used for correct pluralization in all languages
|
|
28
|
+
- RTL languages (Arabic, Hebrew, Persian, Urdu) must be fully supported
|
|
29
|
+
- All files must be UTF-8 encoded — no exceptions, no legacy encoding support
|
|
30
|
+
- English (en) is always the fallback locale
|
|
31
|
+
- Missing translations must fall back gracefully and never crash the application
|
|
32
|
+
- Locale resolution order is explicit flag > config file > LANG env > English fallback
|
|
33
|
+
- All I/O assumes UTF-8 (enforced at startup with a terminal capability check)
|
|
34
|
+
|
|
35
|
+
acceptance_criteria:
|
|
36
|
+
- locales/en.yml exists with complete English translations for all UI strings
|
|
37
|
+
- bin/raictl compile --locales produces gettext and Qt Linguist files
|
|
38
|
+
- bin/raictl --locale fr displays French strings (with English fallback for missing keys)
|
|
39
|
+
- Pluralization works correctly for Arabic (6 forms) and Polish (4 forms)
|
|
40
|
+
- RTL text direction is correctly applied when locale is ar, he, fa, or ur
|
|
41
|
+
- Terminal UTF-8 capability is checked at startup with clear error if unsupported
|
|
42
|
+
- bin/raictl documentation translate --from en --to fr produces draft translation
|
|
43
|
+
- Fallback chain works correctly (nl_BE > nl > en)
|
|
44
|
+
|
|
45
|
+
examples:
|
|
46
|
+
- scenario: "User runs bin/raictl compile on a French system (LANG=fr_FR.UTF-8)"
|
|
47
|
+
expected: |
|
|
48
|
+
CLI output is in French. 'Compiled 3 rules successfully' becomes
|
|
49
|
+
'3 règles compilées avec succès'. Validation errors are in French.
|
|
50
|
+
not: "Output is in English despite French locale. Mixed English/French output."
|
|
51
|
+
- scenario: "Arabic translation has 6 plural forms for 'rules compiled'"
|
|
52
|
+
expected: |
|
|
53
|
+
0 rules: zero form. 1 rule: one form. 2 rules: two form.
|
|
54
|
+
3-10 rules: few form. 11-99 rules: many form. 100+ rules: other form.
|
|
55
|
+
not: "Only singular/plural distinction. Wrong form used for Arabic numbers."
|
|
56
|
+
- scenario: "Developer adds a new CLI command but forgets to add locale strings"
|
|
57
|
+
expected: |
|
|
58
|
+
English key is used as fallback. Log warning about missing translation
|
|
59
|
+
key in development mode. rubocop-i18n (if available) flags hardcoded strings.
|
|
60
|
+
not: "Application crashes with MissingTranslationData error."
|
|
61
|
+
- scenario: "User wants to help translate rosett-ai to their language"
|
|
62
|
+
expected: |
|
|
63
|
+
Copy locales/en.yml to locales/xx.yml. Translate strings. Submit PR.
|
|
64
|
+
bin/raictl compile --locales generates platform-native files from new locale.
|
|
65
|
+
not: "Translator must understand gettext .po format or Qt Linguist .ts format."
|
|
66
|
+
|
|
67
|
+
anti_patterns:
|
|
68
|
+
- Hardcoded user-facing strings in source code
|
|
69
|
+
- Using gettext or Qt Linguist as source format (these are compilation targets)
|
|
70
|
+
- Assuming English plural rules (singular/plural only) for all languages
|
|
71
|
+
- Ignoring RTL text direction for Arabic, Hebrew, Persian, Urdu
|
|
72
|
+
- Legacy encoding support (Shift_JIS, ISO-8859-1, etc.)
|
|
73
|
+
- Crashing on missing translation keys instead of falling back
|
|
74
|
+
- Locale detection that ignores system LANG environment variable
|
|
75
|
+
|
|
76
|
+
preferences:
|
|
77
|
+
language: ruby
|
|
78
|
+
gems:
|
|
79
|
+
- i18n
|
|
80
|
+
- gettext
|
|
81
|
+
patterns:
|
|
82
|
+
- single_source_of_truth_locale_files
|
|
83
|
+
- compile_to_platform_native
|
|
84
|
+
- graceful_fallback_chain
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: integration_testing
|
|
3
|
+
domain: core
|
|
4
|
+
version: "1.1.0"
|
|
5
|
+
status: implemented
|
|
6
|
+
intent: |
|
|
7
|
+
Define a comprehensive integration testing strategy for rosett-ai that ensures
|
|
8
|
+
.deb packages are built from source and verified against InSpec controls
|
|
9
|
+
inside isolated Vagrant/libvirt VMs. The strategy supports both local developer
|
|
10
|
+
workstations and GitLab CI, and is gated so that no CLI change can land on
|
|
11
|
+
main without passing integration verification.
|
|
12
|
+
|
|
13
|
+
This design addresses a systemic gap discovered on 2026-04-07/08: all 37
|
|
14
|
+
InSpec controls were authored speculatively between 2026-03-09 and
|
|
15
|
+
2026-03-23, never executed against a real VM, and accumulated 26 breaking
|
|
16
|
+
CLI changes over 30 days before the first real run. Three controls were
|
|
17
|
+
wrong at the moment they were written.
|
|
18
|
+
|
|
19
|
+
constraints:
|
|
20
|
+
- "Must use Vagrant with libvirt provider (KVM/QEMU) for isolation"
|
|
21
|
+
- "Must install pre-built .deb from pkg/ directory (data_path in kitchen.yml)"
|
|
22
|
+
- "Must install and test ALL engine .debs when present"
|
|
23
|
+
- "Must run as a GitLab CI stage, blocking merge to main on failure"
|
|
24
|
+
- "Must be runnable locally via a single command"
|
|
25
|
+
- "Provisioner must handle locale, ownership, and build deps automatically"
|
|
26
|
+
- "CI job must complete in under 20 minutes"
|
|
27
|
+
- "Must detect when CLI changes break InSpec controls before merge"
|
|
28
|
+
- "kitchen-vagrant pinned to 1.14.2 (last MPL-2.0 compatible with Vagrant 2.3.x)"
|
|
29
|
+
|
|
30
|
+
acceptance_criteria:
|
|
31
|
+
- "`rake build:package && BUNDLE_GEMFILE=Gemfile.integration bundle exec kitchen test` passes all controls"
|
|
32
|
+
- "GitLab CI includes an integration stage with kitchen verify"
|
|
33
|
+
- "kitchen.yml uses vagrant driver with libvirt provider"
|
|
34
|
+
- "README or INSTALL.md documents how to run integration tests locally"
|
|
35
|
+
|
|
36
|
+
examples:
|
|
37
|
+
- scenario: "Developer modifies Thor subcommand structure"
|
|
38
|
+
expected: "CI warns about stale InSpec controls referencing old command names"
|
|
39
|
+
- scenario: "CI runs on merge request to main"
|
|
40
|
+
expected: "Integration stage installs .deb in clean VM, runs kitchen verify, blocks merge on failure"
|
|
41
|
+
- scenario: "Developer runs integration tests locally"
|
|
42
|
+
expected: "`BUNDLE_GEMFILE=Gemfile.integration bundle exec kitchen test` completes via Vagrant/libvirt"
|
|
43
|
+
|
|
44
|
+
preferences:
|
|
45
|
+
language: ruby
|
|
46
|
+
patterns:
|
|
47
|
+
- "kitchen.yml uses vagrant driver with libvirt provider"
|
|
48
|
+
- "Two suites: rai-context (path resolution) and rai-commands (CLI commands)"
|
|
49
|
+
- "provision.sh is the single source of truth for VM setup"
|
|
50
|
+
- "data_path: pkg syncs pre-built .deb packages to guest"
|
|
51
|
+
testing: "InSpec 4.x via kitchen-inspec"
|
|
52
|
+
|
|
53
|
+
anti_patterns:
|
|
54
|
+
- "Do not write InSpec controls without running them against a real target"
|
|
55
|
+
- "Do not modify CLI subcommand structure without grepping test/integration/"
|
|
56
|
+
- "Do not use sudo in InSpec verifier (breaks shell builtins)"
|