ruff-sync 0.1.6.dev1__tar.gz → 0.1.6.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/plans/issue-116-config-validation.md +6 -6
  2. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/ruff-sync-usage/SKILL.md +3 -2
  3. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/ruff-sync-usage/references/troubleshooting.md +20 -0
  4. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/AGENTS.md +1 -0
  5. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/PKG-INFO +1 -1
  6. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/configuration.md +3 -0
  7. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/usage.md +4 -1
  8. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/pyproject.toml +1 -1
  9. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/core.py +1 -1
  10. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/validation.py +102 -4
  11. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_config_validation.py +164 -2
  12. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/uv.lock +1 -1
  13. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/DEPENDENCIES.md +0 -0
  14. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/TESTING.md +0 -0
  15. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/decisions/0001-type-refactoring-strategy.md +0 -0
  16. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/decisions/0002-tui-node-ast.md +0 -0
  17. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/decisions/README.md +0 -0
  18. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/doc-fix.md +0 -0
  19. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/formatters-architecture.md +0 -0
  20. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/gitlab-reports.md +0 -0
  21. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/issue-102-context.md +0 -0
  22. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/plans/issue-100-roadmap-plan.md +0 -0
  23. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/ruff.toml +0 -0
  24. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/adr/SKILL.md +0 -0
  25. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/dirty-equals/SKILL.md +0 -0
  26. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/dirty-equals/references/common-matchers.md +0 -0
  27. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/dirty-equals/references/toml-matching.md +0 -0
  28. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/dirty-equals/trigger_eval.json +0 -0
  29. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/gh-issues/SKILL.md +0 -0
  30. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/mike/SKILL.md +0 -0
  31. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/mike/references/commands.md +0 -0
  32. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/mkdocs-generation/SKILL.md +0 -0
  33. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/mkdocs-generation/examples.md +0 -0
  34. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/mkdocs-generation/templates/api-reference.md +0 -0
  35. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/mkdocs-generation/templates/getting-started.md +0 -0
  36. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/mkdocs-generation/templates/index.md +0 -0
  37. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/mkdocs-generation/templates/mkdocs.yml +0 -0
  38. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/release-notes-generation/SKILL.md +0 -0
  39. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/ruff-sync-usage/references/ci-integration.md +0 -0
  40. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/ruff-sync-usage/references/configuration.md +0 -0
  41. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/skill-creator/SKILL.md +0 -0
  42. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/skill-creator/references/best-practices.md +0 -0
  43. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/skill-creator/references/evaluating-skills.md +0 -0
  44. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/skill-creator/references/optimizing-descriptions.md +0 -0
  45. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/skill-creator/references/quickstart.md +0 -0
  46. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/skill-creator/references/sources.md +0 -0
  47. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/skill-creator/references/using-scripts.md +0 -0
  48. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/skill-creator/scripts/scaffold_skill.py +0 -0
  49. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/textual/SKILL.md +0 -0
  50. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/textual/examples/basic_app.py +0 -0
  51. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/textual/examples/reactive_example.py +0 -0
  52. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/textual/references/events.md +0 -0
  53. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/textual/references/styling.md +0 -0
  54. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/textual/references/testing.md +0 -0
  55. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/textual/references/widgets.md +0 -0
  56. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/type-checking/SKILL.md +0 -0
  57. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/type-checking/evals.json +0 -0
  58. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/type-checking/references/advanced-narrowing.md +0 -0
  59. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/type-checking/references/error-code-lookup.md +0 -0
  60. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/type-checking/references/generics.md +0 -0
  61. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/type-checking/references/naming.md +0 -0
  62. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/type-checking/references/protocol-patterns.md +0 -0
  63. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/type-checking/references/quickstart.md +0 -0
  64. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/type-checking/references/refactoring-patterns.md +0 -0
  65. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/type-checking/scripts/audit_types.py +0 -0
  66. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/skills/warnings-control/SKILL.md +0 -0
  67. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/tui_design.md +0 -0
  68. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/tui_requirements.md +0 -0
  69. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/tui_rule_browsing.md +0 -0
  70. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/tui_rule_browsing_design.md +0 -0
  71. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/workflows/add-test-case.md +0 -0
  72. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.agents/workflows/update-screenshots.md +0 -0
  73. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.git-blame-ignore-revs +0 -0
  74. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.github/dependabot.yml +0 -0
  75. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.github/workflows/ci.yaml +0 -0
  76. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.github/workflows/complexity.yaml +0 -0
  77. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.gitignore +0 -0
  78. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.pre-commit-config.yaml +0 -0
  79. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/.pre-commit-hooks.yaml +0 -0
  80. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/CONTRIBUTING.md +0 -0
  81. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/LICENSE.md +0 -0
  82. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/README.md +0 -0
  83. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/codecov.yml +0 -0
  84. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/configs/data-science-engineering/ruff.toml +0 -0
  85. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/configs/fastapi/ruff.toml +0 -0
  86. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/configs/kitchen-sink/ruff.toml +0 -0
  87. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/agent-skill.md +0 -0
  88. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/assets/favicon.png +0 -0
  89. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/assets/github-job-summary.png +0 -0
  90. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/assets/github-pr-annotation.png +0 -0
  91. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/assets/inspect-main.png +0 -0
  92. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/assets/inspect-search.png +0 -0
  93. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/assets/logo.png +0 -0
  94. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/assets/ruff_sync_banner.png +0 -0
  95. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/assets/screenshots/dashboard.svg +0 -0
  96. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/assets/screenshots/legend_help.svg +0 -0
  97. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/assets/screenshots/rule_details.svg +0 -0
  98. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/assets/screenshots/screenshot_sample.toml +0 -0
  99. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/assets/screenshots/search_omnibox.svg +0 -0
  100. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/best-practices.md +0 -0
  101. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/ci-integration.md +0 -0
  102. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/contributing.md +0 -0
  103. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/examples/advanced-config.toml +0 -0
  104. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/examples/basic-config.toml +0 -0
  105. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/gen_ref_pages.py +0 -0
  106. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/index.md +0 -0
  107. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/inspect.md +0 -0
  108. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/installation.md +0 -0
  109. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/overrides/main.html +0 -0
  110. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/overrides/partials/version_warning.html +0 -0
  111. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/pre-commit.md +0 -0
  112. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/pre-defined-configs.md +0 -0
  113. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/troubleshooting.md +0 -0
  114. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/docs/url-resolution.md +0 -0
  115. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/mkdocs.yml +0 -0
  116. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/scripts/check_dogfood.sh +0 -0
  117. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/scripts/generate_tui_screenshots.py +0 -0
  118. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/scripts/gitclone_dogfood.sh +0 -0
  119. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/scripts/pull_dogfood.sh +0 -0
  120. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/skills-lock.json +0 -0
  121. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/__init__.py +0 -0
  122. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/__main__.py +0 -0
  123. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/cli.py +0 -0
  124. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/config_io.py +0 -0
  125. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/constants.py +0 -0
  126. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/dependencies.py +0 -0
  127. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/formatters.py +0 -0
  128. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/pre_commit.py +0 -0
  129. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/system.py +0 -0
  130. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/tui/__init__.py +0 -0
  131. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/tui/app.py +0 -0
  132. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/tui/constants.py +0 -0
  133. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/tui/screens.py +0 -0
  134. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/tui/themes.py +0 -0
  135. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/tui/types_.py +0 -0
  136. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/tui/widgets.py +0 -0
  137. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/src/ruff_sync/types_.py +0 -0
  138. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tasks.py +0 -0
  139. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/__init__.py +0 -0
  140. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/conftest.py +0 -0
  141. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/multi_upstream_final.toml +0 -0
  142. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/multi_upstream_initial.toml +0 -0
  143. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/multi_upstream_up1.toml +0 -0
  144. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/multi_upstream_up2.toml +0 -0
  145. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/no_changes_final.toml +0 -0
  146. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/no_changes_initial.toml +0 -0
  147. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/no_changes_upstream.toml +0 -0
  148. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/no_dotted_keys_final.toml +0 -0
  149. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/no_dotted_keys_initial.toml +0 -0
  150. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/no_dotted_keys_upstream.toml +0 -0
  151. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/no_ruff_cfg_final.toml +0 -0
  152. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/no_ruff_cfg_initial.toml +0 -0
  153. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/no_ruff_cfg_upstream.toml +0 -0
  154. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/readme_excludes_final.toml +0 -0
  155. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/readme_excludes_initial.toml +0 -0
  156. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/readme_excludes_upstream.toml +0 -0
  157. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/standard_final.toml +0 -0
  158. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/standard_initial.toml +0 -0
  159. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/lifecycle_tomls/standard_upstream.toml +0 -0
  160. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/ruff.toml +0 -0
  161. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_basic.py +0 -0
  162. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_check.py +0 -0
  163. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_ci_integration.py +0 -0
  164. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_ci_validation.py +0 -0
  165. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_config_io.py +0 -0
  166. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_constants.py +0 -0
  167. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_corner_cases.py +0 -0
  168. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_dependencies.py +0 -0
  169. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_deprecation.py +0 -0
  170. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_e2e.py +0 -0
  171. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_formatters.py +0 -0
  172. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_git_fetch.py +0 -0
  173. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_minimal_imports.sh +0 -0
  174. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_pre_commit.py +0 -0
  175. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_project.py +0 -0
  176. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_rule_logic.py +0 -0
  177. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_scaffold.py +0 -0
  178. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_serialization.py +0 -0
  179. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_system.py +0 -0
  180. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_toml_operations.py +0 -0
  181. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_url_handling.py +0 -0
  182. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/test_whitespace.py +0 -0
  183. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/tui/__init__.py +0 -0
  184. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/tui/conftest.py +0 -0
  185. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/tui/test_themes.py +0 -0
  186. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/tui/test_tui.py +0 -0
  187. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/tui/test_tui_types.py +0 -0
  188. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/w_ruff_sync_cfg/pyproject.toml +0 -0
  189. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/wo_ruff_cfg/pyproject.toml +0 -0
  190. {ruff_sync-0.1.6.dev1 → ruff_sync-0.1.6.dev2}/tests/wo_ruff_sync_cfg/pyproject.toml +0 -0
@@ -24,12 +24,12 @@ passing both.
24
24
  > Use this to pick your agent tier. Simple phases can be handled by a weak/cheap model;
25
25
  > Complex phases need a capable reasoning model.
26
26
 
27
- | Priority | Label | Complexity | Feature | Scope |
28
- |---|---|---|---|---|
29
- | 1 (Must Have) | 🔴 | 🟡 Moderate | TOML syntax + Ruff CLI validation | New `validation.py` module; gated by `--validate` in `pull()` |
30
- | 2 (Should Have) | 🟠 | 🟢 Simple | Python version consistency check | `validation.py` + warn when `--validate` is active |
31
- | 3 (Could Have) | 🟡 | 🔴 Complex | Rule deprecation warnings | `validation.py` + subprocess + JSON + DI; gated by `--validate` |
32
- | 4 (Nice to Have) | 🟢 | 🟢 Simple | `--strict` flag | Upgrades warnings to failures; implies `--validate` |
27
+ | Priority | Label | Complexity | Feature | Scope | Status |
28
+ |---|---|---|---|---|---|
29
+ | 1 (Must Have) | 🔴 | 🟡 Moderate | TOML syntax + Ruff CLI validation | New `validation.py` module; gated by `--validate` in `pull()` | ✅ Completed |
30
+ | 2 (Should Have) | 🟠 | 🟢 Simple | Python version consistency check | `validation.py` + warn when `--validate` is active | ✅ Completed |
31
+ | 3 (Could Have) | 🟡 | 🔴 Complex | Rule deprecation warnings | `validation.py` + subprocess + JSON + DI; gated by `--validate` | ⏳ Pending |
32
+ | 4 (Nice to Have) | 🟢 | 🟢 Simple | `--strict` flag | Upgrades warnings to failures; implies `--validate` | 🚧 In Progress |
33
33
 
34
34
  ---
35
35
 
@@ -127,8 +127,9 @@ ruff-sync git@github.com:my-org/standards.git # SSH (shallow clone)
127
127
  ## Gotchas
128
128
 
129
129
  - **`exclude` uses dotted paths, not TOML paths.** `lint.per-file-ignores` refers to the `per-file-ignores` key inside `[tool.ruff.lint]`. Do NOT write `tool.ruff.lint.per-file-ignores`.
130
- - **`--validate` is opt-in.** Validation is off by default. Pass `--validate` (or `--strict`) to run the merged config through Ruff before writing. The sync is aborted if Ruff rejects the config, leaving the local file untouched.
131
- - **`--strict` implies `--validate`.** If you want warnings treated as failures, only `--strict` is needed don't pass both.
130
+ - **`--validate` is opt-in.** Validation is off by default. Pass `--validate` (or `--strict`) to run the merged config through Ruff before writing. The sync is aborted if Ruff rejects the config, leaving the local file untouched. This includes a **Python version consistency check** that compares upstream `target-version` against local `requires-python`.
131
+ - **Excluding `target-version` skips consistency validation.** If `target-version` (or `tool.ruff.target-version`) is in the `exclude` list, `ruff-sync` will skip the version consistency check and log a warning. This is useful for projects that intentionally deviate from upstream Python versions.
132
+ - **`--strict` implies `--validate`.** If you want warnings (like version mismatches or deprecated rules) treated as failures, only `--strict` is needed — don't pass both.
132
133
  - **Validation soft-fails if `ruff` is not on PATH.** If the `ruff` binary can't be found, a warning is logged and the sync continues normally.
133
134
  - **Use `--init` only for new projects.** `ruff-sync` requires an existing `pyproject.toml` or `ruff.toml`. Pass `--init` to scaffold one if the directory is empty. This will automatically generate a `[tool.ruff-sync]` configuration block for future syncs.
134
135
  - **Use `--save` to persist CLI arguments.** If you want to update an existing `pyproject.toml` with a new upstream URL or exclusion, pass `--save` to write the new configuration to the file.
@@ -77,6 +77,26 @@ ruff-sync # pulls config AND updates the hook rev (if pre-commit-versio
77
77
  ```
78
78
 
79
79
  To suppress this check entirely, omit `--pre-commit` from `ruff-sync check` or set `pre-commit-version-sync = false`.
80
+ ---
81
+
82
+ ## Python Version Mismatch Warning
83
+
84
+ **Symptom:** Sync succeeds but logs a warning like:
85
+ ```
86
+ ⚠️ Version mismatch: upstream [tool.ruff] target-version='py39' targets Python 3.9, but local [project] requires-python='>=3.10' requires Python >= 3.10.
87
+ ```
88
+
89
+ **Cause:** The upstream configuration is targeting an older Python version than your project requires. This can lead to Ruff not using newer rules or features available to your project.
90
+
91
+ **Fix:**
92
+ 1. **Update Upstream:** The ideal fix is to update the `target-version` in the upstream repository to a newer version (e.g., `py311`).
93
+ 2. **Exclude the key:** If you need to stay on a specific version locally, add `target-version` to your `exclude` list:
94
+ ```toml
95
+ [tool.ruff-sync]
96
+ exclude = ["target-version"]
97
+ ```
98
+
99
+ In **`--strict`** mode, this warning is upgraded to a hard failure and will abort the sync.
80
100
 
81
101
  ---
82
102
 
@@ -20,6 +20,7 @@ Specific workflows, libraries, and tools are documented in `.agents/skills/`. Be
20
20
 
21
21
  - **Python** ≥ 3.10 (target version `py310`)
22
22
  - **Package Manager**: [uv](https://docs.astral.sh/uv/) — Use `uv run <command>` for all executions to ensure the correct environment.
23
+ - **Note on PATH**: On macOS, `uv` is often installed in `~/.local/bin`. If `uv` is not found, add this to your `PATH`: `export PATH="$PATH:$HOME/.local/bin"`.
23
24
  - **Linter / Formatter**: [Ruff](https://docs.astral.sh/ruff/) (`>=0.15.0`)
24
25
  - **Type Checker**: [mypy](https://mypy-lang.org/) (strict mode)
25
26
  - **Test Framework**: [pytest](https://docs.pytest.org/) with `pytest-asyncio`, `respx`, `pyfakefs` (See [Testing Standards](.agents/TESTING.md))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ruff-sync
3
- Version: 0.1.6.dev1
3
+ Version: 0.1.6.dev2
4
4
  Summary: Synchronize Ruff linter configuration across projects
5
5
  Project-URL: Homepage, https://github.com/Kilo59/ruff-sync
6
6
  Project-URL: Documentation, https://kilo59.github.io/ruff-sync/
@@ -62,6 +62,9 @@ If your projects are on different Python versions but share linting rules:
62
62
  exclude = ["target-version"]
63
63
  ```
64
64
 
65
+ > [!NOTE]
66
+ > Excluding `target-version` also automatically skips the Python version consistency check otherwise performed during `--validate` or `--strict` runs.
67
+
65
68
  #### Sequential merging of multiple sources
66
69
 
67
70
  You can specify multiple upstream sources as a list. They will be merged in order—from top to bottom—with later sources overriding or extending earlier ones.
@@ -122,12 +122,15 @@ ruff-sync --validate
122
122
 
123
123
  If Ruff rejects the merged config (e.g., because the upstream introduced an unknown key), the sync is aborted and your local file is left untouched.
124
124
 
125
- For stricter enforcement, use `--strict`. This upgrades any validation *warnings* such as a Python version mismatch between `[tool.ruff] target-version` and `[project] requires-python` — into hard failures:
125
+ For stricter enforcement, use `--strict`. This upgrades any validation *warnings* into hard failures. This includes a **Python version consistency check** that compares the upstream `[tool.ruff] target-version` against your local `[project] requires-python`:
126
126
 
127
127
  ```bash
128
128
  ruff-sync --strict
129
129
  ```
130
130
 
131
+ > [!TIP]
132
+ > If you intentionally manage your Python version locally and it differs from the upstream, you can add `target-version` to your `exclude` list. `ruff-sync` will then skip the consistency check and log a warning explaining why.
133
+
131
134
  > [!NOTE]
132
135
  > `--strict` implies `--validate`. You do not need to pass both flags.
133
136
  >
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ruff-sync"
3
- version = "0.1.6.dev1"
3
+ version = "0.1.6.dev2"
4
4
  description = "Synchronize Ruff linter configuration across projects"
5
5
  keywords = ["ruff", "linter", "config", "synchronize", "python", "linting", "automation", "tomlkit", "pre-commit"]
6
6
  authors = [
@@ -1164,7 +1164,7 @@ async def pull(
1164
1164
 
1165
1165
  is_ruff_toml = is_ruff_toml_file(_source_toml_path.name)
1166
1166
  if not validate_merged_config(
1167
- source_doc, is_ruff_toml=is_ruff_toml, strict=args.strict
1167
+ source_doc, is_ruff_toml=is_ruff_toml, strict=args.strict, exclude=args.exclude
1168
1168
  ):
1169
1169
  fmt.error(
1170
1170
  "❌ Merged config failed validation. Local file left unchanged.",
@@ -4,12 +4,17 @@ from __future__ import annotations
4
4
 
5
5
  import logging
6
6
  import pathlib
7
+ import re
7
8
  import subprocess
8
9
  import tempfile
10
+ from typing import TYPE_CHECKING
9
11
 
10
12
  import tomlkit
11
13
  from tomlkit import TOMLDocument
12
14
 
15
+ if TYPE_CHECKING:
16
+ from collections.abc import Iterable
17
+
13
18
  __all__ = [
14
19
  "validate_merged_config",
15
20
  "validate_ruff_accepts_config",
@@ -18,6 +23,91 @@ __all__ = [
18
23
 
19
24
  LOGGER = logging.getLogger(__name__)
20
25
 
26
+ _RUFF_TARGET_VERSION_PATTERN = re.compile(r"^py(\d)(\d+)$")
27
+
28
+
29
+ def _ruff_target_to_tuple(target_version: str) -> tuple[int, int] | None:
30
+ """Parse a Ruff target-version string (e.g. 'py311') into a (major, minor) tuple."""
31
+ m = _RUFF_TARGET_VERSION_PATTERN.match(target_version)
32
+ if not m:
33
+ return None
34
+ return int(m.group(1)), int(m.group(2))
35
+
36
+
37
+ def _requires_python_min_version(requires_python: str) -> tuple[int, int] | None:
38
+ """Extract the minimum Python version from a PEP 440 requires-python string.
39
+
40
+ Examples:
41
+ '>=3.10' -> (3, 10)
42
+ '^3.11' -> (3, 11)
43
+ '~=3.9' -> (3, 9)
44
+ """
45
+ # TODO: Consider using packaging.specifiers.SpecifierSet optionally if available
46
+ # Match all version specifiers of the form X.Y
47
+ matches = re.findall(r"(\d+)\.(\d+)", requires_python)
48
+ if matches:
49
+ # Convert to list of (int, int) and return the minimum semantic version
50
+ versions = [(int(major), int(minor)) for major, minor in matches]
51
+ return min(versions)
52
+ return None
53
+
54
+
55
+ def check_python_version_consistency(
56
+ doc: TOMLDocument, strict: bool = False, exclude: Iterable[str] = ()
57
+ ) -> bool:
58
+ """Warn if the merged ruff target-version conflicts with requires-python.
59
+
60
+ Args:
61
+ doc: The merged TOML document (pyproject.toml format).
62
+ strict: If True, treat version mismatch as a failure.
63
+ exclude: List of keys excluded from the ruff configuration.
64
+
65
+ Returns:
66
+ True if versions are consistent or if check is skipped.
67
+ False if strict=True and versions are inconsistent.
68
+ """
69
+ if "target-version" in exclude or "tool.ruff.target-version" in exclude:
70
+ LOGGER.warning(
71
+ "Skipping Python version consistency check: 'target-version' is "
72
+ "excluded in [tool.ruff-sync]."
73
+ )
74
+ return True
75
+ try:
76
+ ruff_section = doc.get("tool", {}).get("ruff", {})
77
+ target_version = ruff_section.get("target-version")
78
+ requires_python = doc.get("project", {}).get("requires-python")
79
+ except (AttributeError, TypeError):
80
+ return True # Don't crash on unexpected doc shapes
81
+
82
+ if not target_version or not requires_python:
83
+ missing = []
84
+ if not target_version:
85
+ missing.append("[tool.ruff] target-version")
86
+ if not requires_python:
87
+ missing.append("[project] requires-python")
88
+ LOGGER.warning(f"Skipping Python version consistency check: missing {', '.join(missing)}.")
89
+ return True # Nothing to compare
90
+
91
+ ruff_min = _ruff_target_to_tuple(str(target_version))
92
+ proj_min = _requires_python_min_version(str(requires_python))
93
+
94
+ if ruff_min is None or proj_min is None:
95
+ return True # Couldn't parse one of the versions
96
+
97
+ if ruff_min < proj_min:
98
+ msg = (
99
+ f"Version mismatch: upstream [tool.ruff] target-version='{target_version}' "
100
+ f"targets Python {ruff_min[0]}.{ruff_min[1]}, but local [project] requires-python="
101
+ f"'{requires_python}' requires Python >= {proj_min[0]}.{proj_min[1]}. "
102
+ "Consider updating target-version in the upstream config."
103
+ )
104
+ if strict:
105
+ LOGGER.error(f"❌ {msg}")
106
+ return False
107
+ LOGGER.warning(f"⚠️ {msg}")
108
+
109
+ return True
110
+
21
111
 
22
112
  def validate_toml_syntax(doc: TOMLDocument) -> bool:
23
113
  """Return True if the document serializes to valid TOML.
@@ -61,8 +151,8 @@ def validate_ruff_accepts_config(
61
151
  dummy_py.write_text("# ruff-sync config validation\n", encoding="utf-8")
62
152
 
63
153
  config_flag = f"--config={tmp_path}"
64
- # Use --isolated to ensure validation is not influenced by local user/project config
65
- cmd = ["ruff", "check", "--isolated", config_flag, str(dummy_py)]
154
+ # Using --config should be enough to isolate from project config
155
+ cmd = ["ruff", "check", config_flag, str(dummy_py)]
66
156
  LOGGER.debug(f"Running ruff config validation: {' '.join(cmd)}")
67
157
 
68
158
  try:
@@ -105,7 +195,10 @@ def validate_ruff_accepts_config(
105
195
 
106
196
 
107
197
  def validate_merged_config(
108
- doc: TOMLDocument, is_ruff_toml: bool = False, strict: bool = False
198
+ doc: TOMLDocument,
199
+ is_ruff_toml: bool = False,
200
+ strict: bool = False,
201
+ exclude: Iterable[str] = (),
109
202
  ) -> bool:
110
203
  """Run all validation checks on the merged TOML document.
111
204
 
@@ -116,10 +209,15 @@ def validate_merged_config(
116
209
  doc: The merged TOML document to validate.
117
210
  is_ruff_toml: True if the document is a standalone ruff.toml.
118
211
  strict: If True, treat configuration warnings as hard failures.
212
+ exclude: List of keys excluded from the ruff configuration.
119
213
 
120
214
  Returns:
121
215
  True if all validation checks pass, False otherwise.
122
216
  """
123
217
  if not validate_toml_syntax(doc):
124
218
  return False
125
- return validate_ruff_accepts_config(doc, is_ruff_toml=is_ruff_toml, strict=strict)
219
+ if not validate_ruff_accepts_config(doc, is_ruff_toml=is_ruff_toml, strict=strict):
220
+ return False
221
+ if not is_ruff_toml:
222
+ return check_python_version_consistency(doc, strict=strict, exclude=exclude)
223
+ return True
@@ -21,6 +21,7 @@ import ruff_sync
21
21
  from ruff_sync import get_config
22
22
  from ruff_sync.cli import LOGGER
23
23
  from ruff_sync.validation import (
24
+ check_python_version_consistency,
24
25
  validate_merged_config,
25
26
  validate_ruff_accepts_config,
26
27
  validate_toml_syntax,
@@ -326,8 +327,8 @@ def test_validate_merged_config_valid(monkeypatch: pytest.MonkeyPatch) -> None:
326
327
  cmd = cast("list[str]", called["cmd"])
327
328
  # Check that some argument refers to a ruff.toml-specific path.
328
329
  assert any("ruff.toml" in str(part) for part in cmd)
329
- # Also verify --isolated is present
330
- assert "--isolated" in cmd
330
+ # verify --isolated is NOT present because it conflicts with --config
331
+ assert "--isolated" not in cmd
331
332
 
332
333
 
333
334
  def test_validate_merged_config_returns_false_on_invalid(
@@ -491,5 +492,166 @@ async def test_pull_succeeds_when_validate_passes(
491
492
  assert "line-length = 100" in source_path.read_text()
492
493
 
493
494
 
495
+ # ===========================================================================
496
+ # Priority 2 — Python Version Consistency Check
497
+ # ===========================================================================
498
+
499
+
500
+ @pytest.mark.parametrize(
501
+ "toml_content, expected_warn, expected_msg",
502
+ [
503
+ pytest.param(
504
+ '[project]\nrequires-python = ">=3.10"\n\n[tool.ruff]\ntarget-version = "py39"\n',
505
+ True,
506
+ "Version mismatch",
507
+ id="mismatch-warn",
508
+ ),
509
+ pytest.param(
510
+ '[project]\nrequires-python = ">=3.10"\n\n[tool.ruff]\ntarget-version = "py310"\n',
511
+ False,
512
+ None,
513
+ id="compatible-exact",
514
+ ),
515
+ pytest.param(
516
+ '[project]\nrequires-python = ">=3.10"\n',
517
+ False,
518
+ None,
519
+ id="missing-target-version",
520
+ ),
521
+ pytest.param(
522
+ '[tool.ruff]\ntarget-version = "py39"\n',
523
+ False,
524
+ None,
525
+ id="missing-requires-python",
526
+ ),
527
+ pytest.param(
528
+ '[project]\nrequires-python = "foo"\n\n[tool.ruff]\ntarget-version = "py310"\n',
529
+ False,
530
+ None,
531
+ id="unparsable-requires-python",
532
+ ),
533
+ pytest.param(
534
+ '[project]\nrequires-python = ">=3.10"\n\n[tool.ruff]\ntarget-version = "3.10"\n',
535
+ False,
536
+ None,
537
+ id="unparsable-target-version",
538
+ ),
539
+ pytest.param(
540
+ '[project]\nrequires-python = "==3.11.*,>=3.8"\n'
541
+ '\n[tool.ruff]\ntarget-version = "py39"\n',
542
+ False,
543
+ None,
544
+ id="multiple-specifiers-ok",
545
+ ),
546
+ pytest.param(
547
+ '[project]\nrequires-python = ">=3.10, <4"\n\n[tool.ruff]\ntarget-version = "py39"\n',
548
+ True,
549
+ "Version mismatch",
550
+ id="multiple-specifiers-warn",
551
+ ),
552
+ ],
553
+ )
554
+ def test_version_consistency_cases(
555
+ caplog: pytest.LogCaptureFixture,
556
+ toml_content: str,
557
+ expected_warn: bool,
558
+ expected_msg: str | None,
559
+ ) -> None:
560
+ """Parametrized test for Python version consistency validation."""
561
+ doc = tomlkit.parse(toml_content)
562
+
563
+ with caplog.at_level(logging.WARNING, logger="ruff_sync.validation"):
564
+ result = check_python_version_consistency(doc)
565
+
566
+ # All these cases should return True (warnings only)
567
+ assert result is True
568
+ if expected_warn:
569
+ assert expected_msg is not None
570
+ assert expected_msg in caplog.text
571
+ else:
572
+ assert "Version mismatch" not in caplog.text
573
+
574
+
575
+ def test_version_consistency_skipped_for_ruff_toml(caplog: pytest.LogCaptureFixture) -> None:
576
+ """Standalone ruff.toml files lack [project], so the check should be skipped."""
577
+ doc = tomlkit.parse("target-version = 'py39'\n")
578
+ # validate_merged_config skips the check if is_ruff_toml=True
579
+ with caplog.at_level(logging.WARNING, logger="ruff_sync.validation"):
580
+ result = validate_merged_config(doc, is_ruff_toml=True)
581
+
582
+ assert result is True
583
+ assert "Version mismatch" not in caplog.text
584
+
585
+
586
+ @pytest.mark.parametrize(
587
+ "toml_content, expected_suffix",
588
+ [
589
+ pytest.param(
590
+ "",
591
+ "missing [tool.ruff] target-version, [project] requires-python.",
592
+ id="missing-both",
593
+ ),
594
+ pytest.param(
595
+ '[project]\nrequires-python = ">=3.10"\n',
596
+ "missing [tool.ruff] target-version.",
597
+ id="missing-target",
598
+ ),
599
+ pytest.param(
600
+ '[tool.ruff]\ntarget-version = "py310"\n',
601
+ "missing [project] requires-python.",
602
+ id="missing-requires",
603
+ ),
604
+ ],
605
+ )
606
+ def test_version_consistency_logs_skip_decision(
607
+ caplog: pytest.LogCaptureFixture, toml_content: str, expected_suffix: str
608
+ ) -> None:
609
+ """It should log a warning message when skipping due to missing version keys."""
610
+ doc = tomlkit.parse(toml_content)
611
+ with caplog.at_level(logging.WARNING, logger="ruff_sync.validation"):
612
+ check_python_version_consistency(doc)
613
+
614
+ expected_msg = f"Skipping Python version consistency check: {expected_suffix}"
615
+ assert expected_msg in caplog.text
616
+
617
+
618
+ def test_version_consistency_skipped_when_excluded(caplog: pytest.LogCaptureFixture) -> None:
619
+ """It should log a specific warning when target-version is explicitly excluded."""
620
+ # mismatch that would normally warn
621
+ toml_content = '[project]\nrequires-python = ">=3.10"\n\n[tool.ruff]\ntarget-version = "py39"\n'
622
+ doc = tomlkit.parse(toml_content)
623
+
624
+ with caplog.at_level(logging.WARNING, logger="ruff_sync.validation"):
625
+ # Case 1: excluded via 'target-version'
626
+ result = check_python_version_consistency(doc, exclude=("target-version",))
627
+ assert result is True
628
+ assert (
629
+ "Skipping Python version consistency check: 'target-version' is "
630
+ "excluded in [tool.ruff-sync]."
631
+ ) in caplog.text
632
+
633
+ caplog.clear()
634
+
635
+ # Case 2: excluded via 'tool.ruff.target-version'
636
+ result = check_python_version_consistency(doc, exclude=("tool.ruff.target-version",))
637
+ assert result is True
638
+ assert (
639
+ "Skipping Python version consistency check: 'target-version' is "
640
+ "excluded in [tool.ruff-sync]."
641
+ ) in caplog.text
642
+
643
+
644
+ def test_strict_mode_fails_on_version_mismatch(caplog: pytest.LogCaptureFixture) -> None:
645
+ """In strict mode, a version mismatch should cause validation to fail."""
646
+ doc = tomlkit.parse(
647
+ '[project]\nrequires-python = ">=3.10"\n\n[tool.ruff]\ntarget-version = "py39"\n'
648
+ )
649
+ with caplog.at_level(logging.ERROR, logger="ruff_sync.validation"):
650
+ result = validate_merged_config(doc, strict=True)
651
+
652
+ assert result is False
653
+ assert "Version mismatch" in caplog.text
654
+
655
+
494
656
  if __name__ == "__main__":
495
657
  pytest.main([__file__, "-vv"])
@@ -1610,7 +1610,7 @@ wheels = [
1610
1610
 
1611
1611
  [[package]]
1612
1612
  name = "ruff-sync"
1613
- version = "0.1.6.dev1"
1613
+ version = "0.1.6.dev2"
1614
1614
  source = { editable = "." }
1615
1615
  dependencies = [
1616
1616
  { name = "httpx" },