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,184 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: doctor
|
|
3
|
+
domain: core
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
status: implemented
|
|
6
|
+
priority: 2
|
|
7
|
+
author: hugo
|
|
8
|
+
created_at: "2026-03-16"
|
|
9
|
+
modified_at: "2026-04-14"
|
|
10
|
+
modified_by: claude opus-4.6
|
|
11
|
+
depends_on:
|
|
12
|
+
- architecture
|
|
13
|
+
- security
|
|
14
|
+
- error_handling
|
|
15
|
+
- engine_architecture
|
|
16
|
+
- monitoring_observability
|
|
17
|
+
#
|
|
18
|
+
intent: |
|
|
19
|
+
Provide a single diagnostic command (`rai doctor`) that checks all runtime
|
|
20
|
+
prerequisites and reports actionable fixes for any issues found. Users
|
|
21
|
+
installing rosett-ai on a new machine, upgrading Ruby, or troubleshooting engine
|
|
22
|
+
failures need a one-stop health check that verifies Ruby version, gem
|
|
23
|
+
dependencies, file permissions, D-Bus availability, engine detection, and
|
|
24
|
+
cache integrity — without requiring them to know which subsystem is failing.
|
|
25
|
+
|
|
26
|
+
Without this command, users troubleshoot by trial and error: "Is it a Ruby
|
|
27
|
+
version mismatch? Missing gems? Wrong permissions?" This design replaces
|
|
28
|
+
guesswork with structured diagnostics that produce a pass/warn/fail report
|
|
29
|
+
per check, with specific remediation steps for each failure.
|
|
30
|
+
|
|
31
|
+
Doctor is the diagnostic layer — it inspects the runtime environment and
|
|
32
|
+
reports readiness. It does not modify state (no auto-fix). Monitoring
|
|
33
|
+
(monitoring_observability.yml) captures runtime telemetry during command
|
|
34
|
+
execution. Error handling (error_handling.yml) governs how errors are
|
|
35
|
+
displayed. Engine detection (engine_architecture.yml) provides the probe
|
|
36
|
+
logic that doctor invokes.
|
|
37
|
+
#
|
|
38
|
+
constraints:
|
|
39
|
+
- "Doctor must be read-only — it inspects and reports but never modifies
|
|
40
|
+
files, installs gems, or changes configuration"
|
|
41
|
+
- "Each check must be independently testable with a clear pass/warn/fail
|
|
42
|
+
verdict and a human-readable remediation message"
|
|
43
|
+
- "Doctor must work without any engine installed — it reports 'no engines
|
|
44
|
+
detected' as a warning, not an error"
|
|
45
|
+
- "Checks must complete within 5 seconds total on a standard system (no
|
|
46
|
+
network calls, no heavy computation)"
|
|
47
|
+
- "Output must be TTY-aware: formatted table with colour when interactive,
|
|
48
|
+
plain text with status codes when piped"
|
|
49
|
+
- "Doctor must not require elevated privileges — all checks operate within
|
|
50
|
+
the user's permission scope"
|
|
51
|
+
- "Exit code 0 when all checks pass, 1 when any check warns, 2 when any
|
|
52
|
+
check fails"
|
|
53
|
+
- "Each check class implements a common interface: #name, #run, #status,
|
|
54
|
+
#message, #remediation"
|
|
55
|
+
- "Checks are registered declaratively and executed in registration order"
|
|
56
|
+
- "Doctor must report the rai version and Ruby version at the top of output"
|
|
57
|
+
- "All remediation messages must be translatable via i18n keys"
|
|
58
|
+
- "This design governs environment diagnostics (checking readiness). Runtime
|
|
59
|
+
telemetry is governed by monitoring_observability.yml. Error display is
|
|
60
|
+
governed by error_handling.yml. Engine probing is governed by
|
|
61
|
+
engine_architecture.yml"
|
|
62
|
+
#
|
|
63
|
+
acceptance_criteria:
|
|
64
|
+
- "`rai doctor` runs all registered checks and displays a summary table"
|
|
65
|
+
- "Ruby version check verifies .ruby-version matches running Ruby"
|
|
66
|
+
- "Gem dependency check verifies all Gemfile.lock gems are installed"
|
|
67
|
+
- "File permission check verifies ~/.claude/ and ~/.config/rosett-ai/ are writable"
|
|
68
|
+
- "D-Bus availability check verifies session bus is accessible (warn if not)"
|
|
69
|
+
- "Engine detection check lists detected engines (warn if none found)"
|
|
70
|
+
- "Cache health check verifies cache directory exists and is writable"
|
|
71
|
+
- "Exit code 0 on all pass, 1 on any warn, 2 on any fail"
|
|
72
|
+
- "TTY-aware output: colour table when interactive, plain lines when piped"
|
|
73
|
+
- "Each check can be run individually via `rai doctor --check ruby_version`"
|
|
74
|
+
- "Output includes rai version and Ruby version header"
|
|
75
|
+
- "All remediation messages use i18n keys from locales/ directory"
|
|
76
|
+
- "`rai doctor --format json` outputs machine-readable results for CI"
|
|
77
|
+
#
|
|
78
|
+
examples:
|
|
79
|
+
- scenario: "User runs `rai doctor` on a correctly configured system"
|
|
80
|
+
expected: |
|
|
81
|
+
raictl 0.9.0 | Ruby 3.3.10 | Linux x86_64
|
|
82
|
+
|
|
83
|
+
┌──────────────────────┬────────┬─────────────────────────────┐
|
|
84
|
+
│ Check │ Status │ Details │
|
|
85
|
+
├──────────────────────┼────────┼─────────────────────────────┤
|
|
86
|
+
│ Ruby version │ PASS │ 3.3.10 matches .ruby-version│
|
|
87
|
+
│ Gem dependencies │ PASS │ All 42 gems installed │
|
|
88
|
+
│ File permissions │ PASS │ ~/.claude/ writable │
|
|
89
|
+
│ D-Bus session bus │ PASS │ be.neatnerds.rosettai reachable │
|
|
90
|
+
│ Engine detection │ PASS │ claude (default), generic │
|
|
91
|
+
│ Cache health │ PASS │ ~/.cache/rosett-ai/ writable │
|
|
92
|
+
└──────────────────────┴────────┴─────────────────────────────┘
|
|
93
|
+
|
|
94
|
+
All 6 checks passed.
|
|
95
|
+
not: |
|
|
96
|
+
Raw exception output. Missing remediation for failures. Exit code 1
|
|
97
|
+
when everything is healthy.
|
|
98
|
+
- scenario: "User runs `rai doctor` with wrong Ruby version installed"
|
|
99
|
+
expected: |
|
|
100
|
+
Ruby version check fails with:
|
|
101
|
+
"FAIL: Ruby 3.2.3 detected, but .ruby-version requires 3.3.10.
|
|
102
|
+
Fix: rbenv install 3.3.10 && rbenv local 3.3.10"
|
|
103
|
+
Exit code 2. Other checks still run and report their status.
|
|
104
|
+
not: |
|
|
105
|
+
Doctor stops at first failure. No remediation suggestion.
|
|
106
|
+
Exit code 1 (should be 2 for failure).
|
|
107
|
+
- scenario: "User runs `rai doctor` on a headless server without D-Bus"
|
|
108
|
+
expected: |
|
|
109
|
+
D-Bus check warns: "WARN: D-Bus session bus not available. Desktop
|
|
110
|
+
integration features will be unavailable.
|
|
111
|
+
Fix: Install dbus and ensure DBUS_SESSION_BUS_ADDRESS is set."
|
|
112
|
+
Exit code 1 (warn). All other checks still run.
|
|
113
|
+
not: |
|
|
114
|
+
Doctor crashes because D-Bus is missing. D-Bus check reports FAIL
|
|
115
|
+
instead of WARN (D-Bus is optional).
|
|
116
|
+
- scenario: "CI pipeline runs `rai doctor --format json`"
|
|
117
|
+
expected: |
|
|
118
|
+
{"rai_version":"0.9.0","ruby_version":"3.3.10","checks":[
|
|
119
|
+
{"name":"ruby_version","status":"pass","message":"3.3.10 matches .ruby-version"},
|
|
120
|
+
{"name":"gem_dependencies","status":"pass","message":"All 42 gems installed"},
|
|
121
|
+
...
|
|
122
|
+
],"summary":{"pass":6,"warn":0,"fail":0}}
|
|
123
|
+
not: "Human-readable table output that CI cannot parse."
|
|
124
|
+
- scenario: "User runs a single check via `rai doctor --check cache_health`"
|
|
125
|
+
expected: |
|
|
126
|
+
Only the cache health check runs. Output shows single check result.
|
|
127
|
+
Exit code reflects that check's status only.
|
|
128
|
+
not: "All checks run despite --check filter. Unknown check name silently ignored."
|
|
129
|
+
#
|
|
130
|
+
anti_patterns:
|
|
131
|
+
- "Auto-fixing problems without user consent (doctor is read-only)"
|
|
132
|
+
- "Requiring network access for any check (all checks must be local)"
|
|
133
|
+
- "Stopping at the first failed check (always run all checks)"
|
|
134
|
+
- "Using raw Ruby exception output instead of structured remediation"
|
|
135
|
+
- "Hardcoding remediation messages instead of using i18n keys"
|
|
136
|
+
- "Treating optional features (D-Bus) as required (warn, not fail)"
|
|
137
|
+
- "Running checks that require elevated privileges"
|
|
138
|
+
- "Dynamic check discovery via ObjectSpace (register checks explicitly)"
|
|
139
|
+
#
|
|
140
|
+
gui_notes: |
|
|
141
|
+
Document interactions (cross-references):
|
|
142
|
+
|
|
143
|
+
1. error_handling.yml: exit codes follow the structured error hierarchy.
|
|
144
|
+
Doctor exit codes (0/1/2) align with the error code mapping.
|
|
145
|
+
|
|
146
|
+
2. engine_architecture.yml: engine detection check invokes the engine
|
|
147
|
+
detector to find installed AI tools.
|
|
148
|
+
|
|
149
|
+
3. monitoring_observability.yml: doctor itself emits telemetry events
|
|
150
|
+
(command_start, command_end) when telemetry is enabled.
|
|
151
|
+
|
|
152
|
+
4. security.yml: file permission checks verify security-relevant paths
|
|
153
|
+
(~/.claude/, ~/.config/rosett-ai/) have correct permissions.
|
|
154
|
+
|
|
155
|
+
5. architecture.yml: check registration follows the pluggable provider
|
|
156
|
+
pattern used throughout rosett-ai.
|
|
157
|
+
|
|
158
|
+
Module structure:
|
|
159
|
+
|
|
160
|
+
lib/rosett_ai/doctor.rb — Diagnostic runner (orchestrates checks)
|
|
161
|
+
lib/rosett_ai/doctor/check.rb — Base check interface
|
|
162
|
+
lib/rosett_ai/doctor/checks/ruby_version_check.rb
|
|
163
|
+
lib/rosett_ai/doctor/checks/gem_dependency_check.rb
|
|
164
|
+
lib/rosett_ai/doctor/checks/file_permission_check.rb
|
|
165
|
+
lib/rosett_ai/doctor/checks/dbus_availability_check.rb
|
|
166
|
+
lib/rosett_ai/doctor/checks/engine_detection_check.rb
|
|
167
|
+
lib/rosett_ai/doctor/checks/cache_health_check.rb
|
|
168
|
+
lib/rosett_ai/thor/tasks/doctor.rb — CLI integration
|
|
169
|
+
#
|
|
170
|
+
preferences:
|
|
171
|
+
language: ruby
|
|
172
|
+
patterns:
|
|
173
|
+
- "Check interface pattern (#name, #run, #status, #message, #remediation)"
|
|
174
|
+
- "Declarative check registration"
|
|
175
|
+
- "TTY-aware output (TtyHelper)"
|
|
176
|
+
- "i18n remediation messages"
|
|
177
|
+
- "JSON output for CI integration"
|
|
178
|
+
testing: rspec with check pass/fail/warn scenarios, TTY vs pipe output,
|
|
179
|
+
exit code verification, JSON output format, and individual check filtering
|
|
180
|
+
gems:
|
|
181
|
+
- thor
|
|
182
|
+
- pastel
|
|
183
|
+
- tty-table
|
|
184
|
+
- i18n
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: documentation
|
|
3
|
+
domain: documentation
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
status: implemented
|
|
6
|
+
priority: 5
|
|
7
|
+
author: claude
|
|
8
|
+
created_at: "2026-02-22"
|
|
9
|
+
modified_at: "2026-03-17"
|
|
10
|
+
modified_by: claude
|
|
11
|
+
depends_on:
|
|
12
|
+
- architecture
|
|
13
|
+
- compiler
|
|
14
|
+
- error_handling
|
|
15
|
+
|
|
16
|
+
intent: |
|
|
17
|
+
Provide end users with complete documentation for installing, configuring,
|
|
18
|
+
and using rosett-ai. The project currently has developer-facing docs (SETUP.md,
|
|
19
|
+
USAGE.md, CONFIGURATION.md) but lacks production-facing artifacts: man pages
|
|
20
|
+
for terminal discovery, installation guides for .deb users, and generated
|
|
21
|
+
API documentation for contributors.
|
|
22
|
+
|
|
23
|
+
Man pages are the Unix standard for command documentation. Users expect
|
|
24
|
+
'ma rai' to work after installing a .deb package. Without this, the tool
|
|
25
|
+
feels incomplete and forces users to find docs online.
|
|
26
|
+
|
|
27
|
+
API documentation via YARD enables contributors to understand the codebase
|
|
28
|
+
without reading every file. This is especially important for an open-source
|
|
29
|
+
GPL-3.0 project that invites community contributions.
|
|
30
|
+
|
|
31
|
+
An installation guide bridges the gap between "here is a .deb file" and
|
|
32
|
+
"here is a working rosett-ai setup". Without it, users must reverse-engineer
|
|
33
|
+
the setup process from developer docs.
|
|
34
|
+
|
|
35
|
+
A quick start guide (doc/QUICK_START.md) provides first-time users with a
|
|
36
|
+
5-minute path from installation to first successful compile. Unlike INSTALL.md
|
|
37
|
+
(which covers all installation methods) or the man page (which is a reference),
|
|
38
|
+
the quick start guide is a tutorial that walks through a single happy-path
|
|
39
|
+
scenario: install, init, author a behaviour, compile, verify. It targets users
|
|
40
|
+
who want to see rosett-ai produce output before reading comprehensive documentation.
|
|
41
|
+
|
|
42
|
+
constraints:
|
|
43
|
+
- Man pages are authored in ronn-ng markdown format (doc/man/*.ronn)
|
|
44
|
+
- Man page build produces roff output at man/man1/rai.1
|
|
45
|
+
- The .deb package ships man pages at /usr/share/man/man1/rai.1.gz
|
|
46
|
+
- "YARD documentation generates without errors; minimum 40% of lib files have YARD tags (target 100%)"
|
|
47
|
+
- All user-facing documentation must be consistent with the current version (no stale version references)
|
|
48
|
+
- Documentation source files are versioned in git alongside code
|
|
49
|
+
- INSTALL.md covers .deb installation, APT repository setup, and post-install verification
|
|
50
|
+
- No documentation references features that don't exist yet (no aspirational docs)
|
|
51
|
+
- ronn-ng is a development dependency only (not runtime)
|
|
52
|
+
- Quick start guide must be completable in under 5 minutes by a new user
|
|
53
|
+
- "Quick start guide covers exactly one path: install, init, author, compile, verify"
|
|
54
|
+
- "Documentation generation processes trusted source files only — no external input accepted"
|
|
55
|
+
|
|
56
|
+
acceptance_criteria:
|
|
57
|
+
- ma rai displays a well-formatted man page after .deb installation
|
|
58
|
+
- doc/man/rai.1.ronn exists with NAME, SYNOPSIS, DESCRIPTION, COMMANDS, OPTIONS, FILES, EXIT STATUS, and SEE ALSO sections
|
|
59
|
+
- A Rake task rake man generates man/man1/rai.1 from ronn source
|
|
60
|
+
- The .deb build process includes man page generation and installs to /usr/share/man/man1/
|
|
61
|
+
- .yardopts configuration exists with project-appropriate settings
|
|
62
|
+
- "yard doc generates HTML documentation; spec/yard_coverage_spec.rb enforces minimum 40% YARD tag coverage (target 100%)"
|
|
63
|
+
- INSTALL.md exists with sections for .deb install, APT repo config, manual install, and verification
|
|
64
|
+
- bin/raictl release prepare verifies documentation consistency (no stale version refs)
|
|
65
|
+
- All subcommands documented in man page match actual CLI help output
|
|
66
|
+
- doc/SETUP.md Ruby version matches .ruby-version (verified by CI)
|
|
67
|
+
- doc/QUICK_START.md exists with install, init, author, compile, verify sections
|
|
68
|
+
- Quick start guide uses only stable commands (no experimental features)
|
|
69
|
+
|
|
70
|
+
examples:
|
|
71
|
+
- scenario: "User installs rosett-ai .deb and wants to learn available commands"
|
|
72
|
+
expected: |
|
|
73
|
+
$ ma rai
|
|
74
|
+
RAICTL(1) NeatNerds Manual RAICTL(1)
|
|
75
|
+
|
|
76
|
+
NAME
|
|
77
|
+
raictl - Rosett-AI
|
|
78
|
+
|
|
79
|
+
SYNOPSIS
|
|
80
|
+
raictl [--accessible] [--locale LOCALE] [--ui ADAPTER] COMMAND [ARGS]
|
|
81
|
+
|
|
82
|
+
DESCRIPTION
|
|
83
|
+
Configuration management tool for Claude Code workflows...
|
|
84
|
+
|
|
85
|
+
COMMANDS
|
|
86
|
+
version Display rosett-ai version
|
|
87
|
+
compile Compile YAML configurations into rule files
|
|
88
|
+
behaviour Behaviour configuration management
|
|
89
|
+
...
|
|
90
|
+
not: |
|
|
91
|
+
'ma rai' returns "No manual entry for Rosett-AI". Man page lists
|
|
92
|
+
commands that don't exist in the CLI. Man page version differs from
|
|
93
|
+
raictl version.
|
|
94
|
+
|
|
95
|
+
- scenario: "Contributor wants to understand the PathResolver API"
|
|
96
|
+
expected: |
|
|
97
|
+
$ yard doc
|
|
98
|
+
Files: 66
|
|
99
|
+
Modules: 15
|
|
100
|
+
Classes: 42
|
|
101
|
+
Methods: 180
|
|
102
|
+
YARD coverage spec enforces >= 40% (target 100%)
|
|
103
|
+
|
|
104
|
+
Open doc/api/index.html in browser → navigate to RosettAi::PathResolver
|
|
105
|
+
→ see all public methods with param types and return values.
|
|
106
|
+
not: |
|
|
107
|
+
YARD coverage drops below 40% threshold. Generated docs reference
|
|
108
|
+
private implementation details.
|
|
109
|
+
|
|
110
|
+
- scenario: "New user wants to install rosett-ai on Debian Bookworm"
|
|
111
|
+
expected: |
|
|
112
|
+
User opens INSTALL.md, finds clear steps:
|
|
113
|
+
1. Add NeatNerds APT repository
|
|
114
|
+
2. Import GPG signing key
|
|
115
|
+
3. apt update && apt install rosett-ai
|
|
116
|
+
4. rai version (verify installation)
|
|
117
|
+
5. rai init --global (first-time setup)
|
|
118
|
+
not: |
|
|
119
|
+
INSTALL.md says "run bundle install" (that's developer setup, not user install).
|
|
120
|
+
INSTALL.md references features not yet available. Steps are out of order.
|
|
121
|
+
|
|
122
|
+
- scenario: "First-time user wants to see rosett-ai produce output immediately"
|
|
123
|
+
expected: |
|
|
124
|
+
User opens doc/QUICK_START.md, follows 5 steps:
|
|
125
|
+
1. apt install rosett-ai (or gem install)
|
|
126
|
+
2. rai init --global
|
|
127
|
+
3. rai behaviour manage add my_first_rule (creates a template)
|
|
128
|
+
4. rai compile --verbose (compiles to ~/.claude/rules/)
|
|
129
|
+
5. cat ~/.claude/rules/my_first_rule.md (sees compiled output)
|
|
130
|
+
Total time: under 5 minutes. User has a working rosett-ai setup.
|
|
131
|
+
not: |
|
|
132
|
+
Quick start requires reading 3 other documents first. Quick start
|
|
133
|
+
uses experimental commands. Quick start assumes prior knowledge of
|
|
134
|
+
YAML schemas or engine architecture.
|
|
135
|
+
|
|
136
|
+
anti_patterns:
|
|
137
|
+
- Writing man pages in raw roff format (unreadable, unmaintainable)
|
|
138
|
+
- Generating documentation that references undocumented or private methods
|
|
139
|
+
- Shipping documentation with stale version numbers
|
|
140
|
+
- Duplicating CLI help text in man pages (generate from single source where possible)
|
|
141
|
+
- Creating documentation for features that don't exist yet (aspirational docs mislead users)
|
|
142
|
+
- Skipping man page from .deb package (users expect Unix conventions)
|
|
143
|
+
|
|
144
|
+
preferences:
|
|
145
|
+
language: ruby
|
|
146
|
+
gems:
|
|
147
|
+
- ronn-ng
|
|
148
|
+
- yard
|
|
149
|
+
patterns:
|
|
150
|
+
- single_source_of_truth
|
|
151
|
+
- version_consistency_verification
|
|
152
|
+
testing: rspec for doc generation tasks, CI linting for version consistency
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: engine_architecture
|
|
3
|
+
domain: core
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
status: implemented
|
|
6
|
+
priority: 2
|
|
7
|
+
author: hugo
|
|
8
|
+
created_at: "2026-02-26"
|
|
9
|
+
modified_at: "2026-04-14"
|
|
10
|
+
modified_by: claude opus-4.6
|
|
11
|
+
depends_on:
|
|
12
|
+
- architecture
|
|
13
|
+
- security
|
|
14
|
+
- testing
|
|
15
|
+
- compiler
|
|
16
|
+
- error_handling
|
|
17
|
+
- scope_hierarchy
|
|
18
|
+
|
|
19
|
+
intent: |
|
|
20
|
+
Establish a pluggable engine architecture that enables rosett-ai to manage
|
|
21
|
+
configuration for multiple AI coding tools from a single set of
|
|
22
|
+
human-authored YAML source files. Each AI tool (Claude Code, Cursor,
|
|
23
|
+
Goose, AGENTS.md, Ollama, GPT-NeoX, etc.) is represented by an
|
|
24
|
+
independent engine — a self-contained gem with optional components.
|
|
25
|
+
|
|
26
|
+
The generic layer (conf/behaviour/, conf/design/) captures human intent:
|
|
27
|
+
behaviour rules, ethics, workflows, coding standards. This layer is
|
|
28
|
+
AI-tool-agnostic. Each engine translates the generic layer into its
|
|
29
|
+
tool's native format and writes to the tool's native filesystem location.
|
|
30
|
+
|
|
31
|
+
Engines are independently installable. Core rosett-ai has no LLM API
|
|
32
|
+
dependencies. The Claude engine brings the anthropic gem. The Ollama
|
|
33
|
+
engine brings an HTTP client for localhost. A minimal installation
|
|
34
|
+
with only the Generic engine produces plain markdown — no AI tool needed.
|
|
35
|
+
|
|
36
|
+
Engine authors need a comprehensive development guide (doc/ENGINE_DEVELOPMENT_GUIDE.md)
|
|
37
|
+
that covers manifest schema, component interfaces, testing patterns, and
|
|
38
|
+
packaging requirements. Without this guide, third-party engine development
|
|
39
|
+
requires reading Rosett-AI core source code — which is fragile and undocumented.
|
|
40
|
+
The guide serves as the Plugin SDK documentation for the engine architecture.
|
|
41
|
+
|
|
42
|
+
Engines are autodetectable. At init time or via bin/raictl engines detect,
|
|
43
|
+
raictl probes the system for installed AI tools and suggests a default
|
|
44
|
+
engine. Engines that support programmatic invocation (API or CLI) can
|
|
45
|
+
participate in quorum analysis via the separate quorum module.
|
|
46
|
+
|
|
47
|
+
This design supersedes the implicit Claude-only assumption and implements
|
|
48
|
+
the direction accepted as ADR-007.
|
|
49
|
+
|
|
50
|
+
constraints:
|
|
51
|
+
- Each engine is a self-contained directory — no cross-engine dependencies
|
|
52
|
+
- Generic layer (behaviour, design YAML) contains only human intent — no engine-specific keys
|
|
53
|
+
- Engine-specific configuration lives in conf/engines/<name>/ — not in generic config
|
|
54
|
+
- Core rosett-ai has zero LLM API dependencies — API gems are engine-specific
|
|
55
|
+
- Existing Claude Code workflows must not break during incremental migration
|
|
56
|
+
- Engine components are optional — only implement what applies to the tool
|
|
57
|
+
- No forced common interface across engines — each implements relevant components only
|
|
58
|
+
- Autodetection is data-driven from manifest.yml — custom Ruby only when needed
|
|
59
|
+
- Quorum is a separate module (lib/rosett_ai/quorum/) — not part of engine interface
|
|
60
|
+
- Security constraints from security.yml apply to all engine code
|
|
61
|
+
- No ERB or eval-based templating for machine-parsed output (per ADR-006)
|
|
62
|
+
- Engine manifest schema must be documented and validated via JSON Schema
|
|
63
|
+
- doc/ENGINE_DEVELOPMENT_GUIDE.md must exist with manifest schema, component
|
|
64
|
+
interfaces, testing patterns, and packaging requirements
|
|
65
|
+
- "Engine target profiles must declare per-scope output directories: an
|
|
66
|
+
absolute path for global output and a relative path for scoped (local/
|
|
67
|
+
project) output resolved against the scope directory — see scope_hierarchy.yml"
|
|
68
|
+
- Engine detection must work at any scope level (global, local, project)
|
|
69
|
+
|
|
70
|
+
acceptance_criteria:
|
|
71
|
+
- lib/rosett_ai/engines/claude/ directory contains backend, config_adapter, detector, and manifest
|
|
72
|
+
- lib/rosett_ai/engines/generic/ directory contains backend and manifest
|
|
73
|
+
- bin/raictl compile --engine claude produces identical output to current bin/raictl compile
|
|
74
|
+
- bin/raictl compile --engine generic produces identical output to current bin/raictl compile --target generic
|
|
75
|
+
- bin/raictl engines list shows all known engines with installed/detected status
|
|
76
|
+
- bin/raictl engines detect probes system and reports which AI tools are present
|
|
77
|
+
- bin/raictl init suggests default engine based on autodetection
|
|
78
|
+
- Compiling for an engine that does not support a generic feature emits a warning
|
|
79
|
+
- bin/raictl compile --engine cursor --strict fails if any unsupported features are present
|
|
80
|
+
- anthropic gem is not in rosett-ai.gemspec runtime dependencies
|
|
81
|
+
- bin/raictl adopt runs local structural checks without any engine or API configured
|
|
82
|
+
- bin/raictl adopt --api --engine claude invokes Claude API for analysis
|
|
83
|
+
- All 251+ existing tests pass after engine restructure with zero behavioural change
|
|
84
|
+
- conf/engines/claude/manifest.yml declares capabilities, detection, and execution metadata
|
|
85
|
+
- conf/schemas/engine_manifest_schema.json validates all engine manifests
|
|
86
|
+
- doc/ENGINE_DEVELOPMENT_GUIDE.md covers manifest schema, component interfaces,
|
|
87
|
+
testing patterns, gem packaging, and CI setup for third-party engines
|
|
88
|
+
- "Engine TargetProfile includes output_dirs hash with global (absolute)
|
|
89
|
+
and scoped (relative) paths"
|
|
90
|
+
- Engine detection works correctly at any scope level — an engine configured
|
|
91
|
+
at global scope is available to all local and project scopes
|
|
92
|
+
|
|
93
|
+
examples:
|
|
94
|
+
- scenario: "User has only Claude Code installed, runs bin/raictl compile"
|
|
95
|
+
expected: |
|
|
96
|
+
Autodetected default engine is claude. Compiles behaviour YAML to
|
|
97
|
+
~/.claude/rules/ in Claude markdown format. Output identical to
|
|
98
|
+
pre-restructure behaviour. No warnings.
|
|
99
|
+
not: "Compilation fails because no --engine flag provided. Output differs from before."
|
|
100
|
+
- scenario: "User installs Cursor alongside Claude, runs bin/raictl compile --engine cursor"
|
|
101
|
+
expected: |
|
|
102
|
+
Compiles behaviour YAML to .cursorrules in project root. Warns about
|
|
103
|
+
unsupported features (priority annotations, sensitive filtering).
|
|
104
|
+
Claude rules in ~/.claude/rules/ are unaffected.
|
|
105
|
+
not: "Cursor compilation modifies Claude rules. Unsupported features silently dropped."
|
|
106
|
+
- scenario: "CI pipeline runs bin/raictl compile --engine agents_md --strict"
|
|
107
|
+
expected: |
|
|
108
|
+
Compiles to AGENTS.md. If any behaviour uses features unsupported by
|
|
109
|
+
AGENTS.md engine, build fails with clear error listing unsupported features.
|
|
110
|
+
not: "Silently produces incomplete AGENTS.md. Build passes despite gaps."
|
|
111
|
+
- scenario: "Air-gapped government server with GPT-NeoX on vLLM, no internet"
|
|
112
|
+
expected: |
|
|
113
|
+
bin/raictl engines detect finds gpt_neox engine (vLLM endpoint configured).
|
|
114
|
+
bin/raictl adopt --api --engine gpt_neox analyses rules via local LLM.
|
|
115
|
+
No network calls to external services. Ollama not required.
|
|
116
|
+
not: "Adopt fails because no Anthropic API key. GPT-NeoX requires Ollama."
|
|
117
|
+
- scenario: "Claude engine declares per-scope output directories"
|
|
118
|
+
expected: |
|
|
119
|
+
Claude engine target profile contains:
|
|
120
|
+
output_dirs:
|
|
121
|
+
global: ~/.claude/rules/
|
|
122
|
+
scoped: .claude/rules/
|
|
123
|
+
bin/raictl compile --project resolves scoped path against project dir:
|
|
124
|
+
~/projects/acme-api/.claude/rules/
|
|
125
|
+
bin/raictl compile --global writes to the absolute path: ~/.claude/rules/.
|
|
126
|
+
not: "All compiled output goes to ~/.claude/rules/ regardless of scope."
|
|
127
|
+
|
|
128
|
+
anti_patterns:
|
|
129
|
+
- Engine B depending on Engine A (e.g. GPT-NeoX requiring Ollama)
|
|
130
|
+
- Generic layer containing engine-specific keys or extensions
|
|
131
|
+
- LLM API gems in core rosett-ai dependencies
|
|
132
|
+
- Assuming any specific engine is installed or configured
|
|
133
|
+
- Forced common interface that all engines must implement
|
|
134
|
+
- Config compilation logic outside the engine that owns the format
|
|
135
|
+
- Hardcoded engine paths instead of manifest-driven detection
|
|
136
|
+
- Hardcoded output directories instead of scope-aware output_dirs
|
|
137
|
+
(global absolute + scoped relative) from engine target profiles
|
|
138
|
+
|
|
139
|
+
gui_notes: |
|
|
140
|
+
Engine directory structure:
|
|
141
|
+
|
|
142
|
+
lib/rosett_ai/engines/<name>/
|
|
143
|
+
backend.rb — Compiler backend (renders YAML → tool-native format)
|
|
144
|
+
config_adapter.rb — Settings compilation (tool-specific config files)
|
|
145
|
+
path_resolver.rb — Output path resolution (where to write)
|
|
146
|
+
scaffolder.rb — Init/scaffold (create tool directory structure)
|
|
147
|
+
detector.rb — Autodetection (is this tool installed?)
|
|
148
|
+
executor.rb — API/CLI invocation (for quorum, adopt --api)
|
|
149
|
+
validator.rb — Output validation (is compiled output correct?)
|
|
150
|
+
manifest.yml — Capability declaration, detection rules, execution metadata
|
|
151
|
+
|
|
152
|
+
All components are optional. Minimal engine (e.g. Cursor):
|
|
153
|
+
backend.rb + detector.rb + manifest.yml
|
|
154
|
+
|
|
155
|
+
Full engine (e.g. Claude):
|
|
156
|
+
All 8 files
|
|
157
|
+
|
|
158
|
+
Manifest structure:
|
|
159
|
+
|
|
160
|
+
name: claude
|
|
161
|
+
display_name: Claude Code
|
|
162
|
+
detection:
|
|
163
|
+
binaries: [claude]
|
|
164
|
+
config_dirs: [~/.claude]
|
|
165
|
+
env_vars: [ANTHROPIC_API_KEY]
|
|
166
|
+
capabilities:
|
|
167
|
+
behaviour_rules: true
|
|
168
|
+
design_constraints: true
|
|
169
|
+
priority_annotations: true
|
|
170
|
+
sensitive_filtering: true
|
|
171
|
+
source_metadata: true
|
|
172
|
+
execution:
|
|
173
|
+
invocable: true
|
|
174
|
+
methods: [api, cli]
|
|
175
|
+
api_gem: anthropic
|
|
176
|
+
cli_binary: claude
|
|
177
|
+
structured_output: true
|
|
178
|
+
output_dirs:
|
|
179
|
+
global: ~/.claude/rules/
|
|
180
|
+
scoped: .claude/rules/
|
|
181
|
+
supported_assets:
|
|
182
|
+
skills: { target: ".claude/skills/", format: "markdown" }
|
|
183
|
+
hooks: { target: ".claude/settings.json#hooks", format: "json" }
|
|
184
|
+
memory: { target: ".claude/memory/", format: "markdown" }
|
|
185
|
+
required_config:
|
|
186
|
+
- mcp_servers
|
|
187
|
+
- permissions
|
|
188
|
+
- api_settings
|
|
189
|
+
|
|
190
|
+
Quorum module (separate from engines):
|
|
191
|
+
|
|
192
|
+
lib/rosett_ai/quorum/
|
|
193
|
+
dispatcher.rb — Send analysis to N engines in parallel
|
|
194
|
+
collector.rb — Gather responses, handle timeouts
|
|
195
|
+
comparator.rb — Determine consensus/disagreements
|
|
196
|
+
strategies/
|
|
197
|
+
adopt.rb — Compare structured adopt findings
|
|
198
|
+
majority.rb — Simple majority vote
|
|
199
|
+
unanimous.rb — All must agree
|
|
200
|
+
|
|
201
|
+
interactions:
|
|
202
|
+
- action: "Compile rules for a specific engine"
|
|
203
|
+
tui: "bin/raictl compile --engine claude"
|
|
204
|
+
- action: "List available engines"
|
|
205
|
+
tui: "bin/raictl engines list"
|
|
206
|
+
- action: "Detect installed AI tools"
|
|
207
|
+
tui: "bin/raictl engines detect"
|
|
208
|
+
- action: "Show engine status"
|
|
209
|
+
tui: "bin/raictl engines status"
|
|
210
|
+
- action: "Run local-only adopt"
|
|
211
|
+
tui: "bin/raictl adopt"
|
|
212
|
+
- action: "Run API-based adopt via specific engine"
|
|
213
|
+
tui: "bin/raictl adopt --api --engine claude"
|
|
214
|
+
- action: "Run quorum adopt"
|
|
215
|
+
tui: "bin/raictl adopt --api --quorum"
|
|
216
|
+
|
|
217
|
+
preferences:
|
|
218
|
+
language: ruby
|
|
219
|
+
patterns:
|
|
220
|
+
- "Engine as namespace directory (lib/rosett_ai/engines/<name>/)"
|
|
221
|
+
- "Capability manifest with asset type contracts"
|
|
222
|
+
- "Data-driven detection from manifest.yml"
|
|
223
|
+
- "Optional components (only implement what applies)"
|
|
224
|
+
- "Strategy pattern for compiler backends"
|
|
225
|
+
- "No forced common interface across engines"
|
|
226
|
+
testing: rspec with per-engine specs, capability gap tests, manifest
|
|
227
|
+
validation, autodetection scenarios, and asset contract verification
|
|
228
|
+
gems:
|
|
229
|
+
- thor
|
|
230
|
+
- json_schemer
|
|
231
|
+
- zeitwerk
|
|
232
|
+
|
|
233
|
+
implementation_notes: |
|
|
234
|
+
v1.2.0 implementation diverged from the original design in key areas:
|
|
235
|
+
|
|
236
|
+
1. Engine code lives in separate gems (rosett-ai-engine-claude,
|
|
237
|
+
rosett-ai-engine-agents-md) discovered via the plugin registry,
|
|
238
|
+
not in lib/rosett_ai/engines/<name>/ directories within core.
|
|
239
|
+
|
|
240
|
+
2. Core provides: base_config_compiler.rb, detector.rb, registry.rb
|
|
241
|
+
as abstract base infrastructure. Engine gems implement the
|
|
242
|
+
concrete backends.
|
|
243
|
+
|
|
244
|
+
3. Compiler backends (claude_backend, agents_md_backend, generic_backend)
|
|
245
|
+
live in lib/rosett_ai/compiler/backends/ as fallback implementations
|
|
246
|
+
for when engine gems are not installed.
|
|
247
|
+
|
|
248
|
+
4. Quorum module not yet implemented — deferred to post-v1.2.0.
|
|
249
|
+
|
|
250
|
+
5. Per-scope output_dirs (global absolute + scoped relative) not yet
|
|
251
|
+
in TargetProfile — uses a single output_dir field.
|
|
252
|
+
|
|
253
|
+
6. doc/ENGINE_DEVELOPMENT_GUIDE.md exists and covers manifest schema,
|
|
254
|
+
component interfaces, and gem packaging.
|
|
255
|
+
|
|
256
|
+
These divergences are intentional: the external-gem model proved
|
|
257
|
+
cleaner for distribution and versioning than embedded directories.
|