ruff-sync 0.1.6.dev3__tar.gz → 0.1.7.dev1__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.
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/TESTING.md +1 -1
- ruff_sync-0.1.7.dev1/.agents/args_refactor_plan.md +443 -0
- ruff_sync-0.1.7.dev1/.agents/cli_animation_plan.md +710 -0
- ruff_sync-0.1.7.dev1/.agents/decisions/0003-argument-resolution-layers.md +120 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/decisions/README.md +1 -0
- ruff_sync-0.1.7.dev1/.agents/docs_update_plan.md +384 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/ruff-sync-usage/references/ci-integration.md +13 -2
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/ruff-sync-usage/references/configuration.md +24 -0
- ruff_sync-0.1.7.dev1/.agents/workflows/update-recordings.md +72 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/PKG-INFO +5 -1
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/README.md +4 -0
- ruff_sync-0.1.7.dev1/docs/assets/recordings/check_drift.gif +0 -0
- ruff_sync-0.1.7.dev1/docs/assets/recordings/check_in_sync.gif +0 -0
- ruff_sync-0.1.7.dev1/docs/assets/recordings/help_overview.gif +0 -0
- ruff_sync-0.1.7.dev1/docs/assets/recordings/init_project.gif +0 -0
- ruff_sync-0.1.7.dev1/docs/assets/recordings/pull_basic.gif +0 -0
- ruff_sync-0.1.7.dev1/docs/assets/recordings/validate_strict.gif +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/ci-integration.md +33 -2
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/configuration.md +19 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/examples/advanced-config.toml +3 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/index.md +5 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/pre-commit.md +3 -3
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/pre-defined-configs.md +4 -4
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/troubleshooting.md +43 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/usage.md +10 -2
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/pyproject.toml +1 -1
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/cli.py +131 -37
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/constants.py +41 -3
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/core.py +34 -17
- ruff_sync-0.1.7.dev1/tapes/_common.tape +21 -0
- ruff_sync-0.1.7.dev1/tapes/check_drift.tape +38 -0
- ruff_sync-0.1.7.dev1/tapes/check_in_sync.tape +20 -0
- ruff_sync-0.1.7.dev1/tapes/help_overview.tape +27 -0
- ruff_sync-0.1.7.dev1/tapes/init_project.tape +36 -0
- ruff_sync-0.1.7.dev1/tapes/pull_basic.tape +32 -0
- ruff_sync-0.1.7.dev1/tapes/validate_strict.tape +24 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tasks.py +55 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/conftest.py +17 -1
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_basic.py +2 -2
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_config_validation.py +315 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_constants.py +30 -1
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_formatters.py +14 -6
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_serialization.py +148 -13
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/uv.lock +1 -1
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/DEPENDENCIES.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/decisions/0001-type-refactoring-strategy.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/decisions/0002-tui-node-ast.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/doc-fix.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/formatters-architecture.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/gitlab-reports.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/issue-102-context.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/plans/issue-100-roadmap-plan.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/plans/issue-116-config-validation.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/ruff.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/adr/SKILL.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/dirty-equals/SKILL.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/dirty-equals/references/common-matchers.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/dirty-equals/references/toml-matching.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/dirty-equals/trigger_eval.json +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/gh-issues/SKILL.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/mike/SKILL.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/mike/references/commands.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/mkdocs-generation/SKILL.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/mkdocs-generation/examples.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/mkdocs-generation/templates/api-reference.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/mkdocs-generation/templates/getting-started.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/mkdocs-generation/templates/index.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/mkdocs-generation/templates/mkdocs.yml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/release-notes-generation/SKILL.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/ruff-sync-usage/SKILL.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/ruff-sync-usage/references/troubleshooting.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/skill-creator/SKILL.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/skill-creator/references/best-practices.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/skill-creator/references/evaluating-skills.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/skill-creator/references/optimizing-descriptions.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/skill-creator/references/quickstart.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/skill-creator/references/sources.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/skill-creator/references/using-scripts.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/skill-creator/scripts/scaffold_skill.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/textual/SKILL.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/textual/examples/basic_app.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/textual/examples/reactive_example.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/textual/references/events.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/textual/references/styling.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/textual/references/testing.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/textual/references/widgets.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/type-checking/SKILL.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/type-checking/evals.json +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/type-checking/references/advanced-narrowing.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/type-checking/references/error-code-lookup.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/type-checking/references/generics.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/type-checking/references/naming.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/type-checking/references/protocol-patterns.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/type-checking/references/quickstart.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/type-checking/references/refactoring-patterns.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/type-checking/scripts/audit_types.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/skills/warnings-control/SKILL.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/tui_design.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/tui_requirements.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/tui_rule_browsing.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/tui_rule_browsing_design.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/workflows/add-test-case.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.agents/workflows/update-screenshots.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.git-blame-ignore-revs +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.github/dependabot.yml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.github/workflows/ci.yaml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.github/workflows/complexity.yaml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.gitignore +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.pre-commit-config.yaml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/.pre-commit-hooks.yaml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/AGENTS.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/CONTRIBUTING.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/LICENSE.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/codecov.yml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/configs/data-science-engineering/ruff.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/configs/fastapi/ruff.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/configs/kitchen-sink/ruff.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/agent-skill.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/assets/favicon.png +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/assets/github-job-summary.png +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/assets/github-pr-annotation.png +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/assets/inspect-main.png +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/assets/inspect-search.png +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/assets/logo.png +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/assets/ruff_sync_banner.png +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/assets/screenshots/dashboard.svg +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/assets/screenshots/legend_help.svg +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/assets/screenshots/rule_details.svg +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/assets/screenshots/screenshot_sample.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/assets/screenshots/search_omnibox.svg +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/best-practices.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/contributing.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/examples/basic-config.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/gen_ref_pages.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/inspect.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/installation.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/overrides/main.html +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/overrides/partials/version_warning.html +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/docs/url-resolution.md +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/mkdocs.yml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/scripts/check_dogfood.sh +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/scripts/generate_tui_screenshots.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/scripts/gitclone_dogfood.sh +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/scripts/pull_dogfood.sh +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/skills-lock.json +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/__init__.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/__main__.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/config_io.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/dependencies.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/formatters.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/pre_commit.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/system.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/tui/__init__.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/tui/app.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/tui/constants.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/tui/screens.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/tui/themes.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/tui/types_.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/tui/widgets.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/types_.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/src/ruff_sync/validation.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/__init__.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/multi_upstream_final.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/multi_upstream_initial.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/multi_upstream_up1.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/multi_upstream_up2.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/no_changes_final.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/no_changes_initial.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/no_changes_upstream.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/no_dotted_keys_final.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/no_dotted_keys_initial.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/no_dotted_keys_upstream.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/no_ruff_cfg_final.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/no_ruff_cfg_initial.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/no_ruff_cfg_upstream.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/readme_excludes_final.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/readme_excludes_initial.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/readme_excludes_upstream.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/standard_final.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/standard_initial.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/lifecycle_tomls/standard_upstream.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/ruff.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_check.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_ci_integration.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_ci_validation.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_config_io.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_corner_cases.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_dependencies.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_deprecation.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_e2e.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_git_fetch.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_minimal_imports.sh +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_pre_commit.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_project.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_rule_logic.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_scaffold.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_system.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_toml_operations.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_url_handling.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/test_whitespace.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/tui/__init__.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/tui/conftest.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/tui/test_themes.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/tui/test_tui.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/tui/test_tui_types.py +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/w_ruff_sync_cfg/pyproject.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/wo_ruff_cfg/pyproject.toml +0 -0
- {ruff_sync-0.1.6.dev3 → ruff_sync-0.1.7.dev1}/tests/wo_ruff_sync_cfg/pyproject.toml +0 -0
|
@@ -5,7 +5,7 @@ This document defines the mandatory testing standards and patterns for the `ruff
|
|
|
5
5
|
## 1. Core Principles
|
|
6
6
|
|
|
7
7
|
- **Every Fix Needs a Test**: Any bug fix must include a reproduction test that fails without the fix and passes with it.
|
|
8
|
-
- **No Side Effects**: Tests must be isolated and not touch the actual filesystem or make real network calls.
|
|
8
|
+
- **No Side Effects**: Tests must be isolated and not touch the actual filesystem or make real network calls. Furthermore, tests must not pollute the CI environment (e.g., writing to `GITHUB_STEP_SUMMARY`). Always mock CI-specific environment variables using `monkeypatch.delenv` if the code under test interacts with them.
|
|
9
9
|
- **Semantic + Structural Assertions**: When testing TOML merges, always verify **both**:
|
|
10
10
|
1. **Structural/Whitespace**: The file "looks" correct (comments and spacing are preserved).
|
|
11
11
|
2. **Semantic**: The actual data in the merged result matches the expected values. Use the [dirty-equals](skills/dirty-equals/SKILL.md) Agent Skill for declarative, concise assertions.
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
# Refactoring Plan: Clean Up `strict` / `validate` / `pre_commit` Default Handling
|
|
2
|
+
|
|
3
|
+
> **Context**: Commit `051ca28` ("low effort handling of strict/validate defaults") changed the
|
|
4
|
+
> `Arguments` NamedTuple to use `bool | MissingType` for `validate`, `strict`, and `pre_commit`,
|
|
5
|
+
> then forced every consumer (`pull`, `check`, `_merge_multiple_upstreams`) to call
|
|
6
|
+
> `resolve_defaults()` at the top of the function and destructure a 6-tuple. This is architecturally
|
|
7
|
+
> bad for several reasons outlined below.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Problem Analysis
|
|
12
|
+
|
|
13
|
+
### What the commit did
|
|
14
|
+
|
|
15
|
+
1. Changed `Arguments.validate`, `Arguments.strict`, and `Arguments.pre_commit` from `bool` to
|
|
16
|
+
`bool | MissingType`.
|
|
17
|
+
2. Expanded `resolve_defaults()` from `(branch, path, exclude) → 3-tuple` to
|
|
18
|
+
`(branch, path, exclude, validate, strict, pre_commit) → 6-tuple`, mixing two concerns (URL
|
|
19
|
+
resolution defaults and boolean flag defaults).
|
|
20
|
+
3. Added `resolve_defaults()` calls to the top of `pull()`, `check()`, and
|
|
21
|
+
`_merge_multiple_upstreams()`, each destructuring the 6-tuple with throw-away `_` variables.
|
|
22
|
+
4. Added `pre_commit` to `ResolvedArgs`, making it carry `bool | MissingType` too.
|
|
23
|
+
|
|
24
|
+
### Why this is bad
|
|
25
|
+
|
|
26
|
+
| Issue | Explanation |
|
|
27
|
+
|-------|-------------|
|
|
28
|
+
| **Sentinel leaks into execution logic** | `pull()` and `check()` don't care about `MISSING`—they just need a `bool`. But now their type signatures lie: `args.validate` is `bool \| MissingType`, so every access site needs a guard or a `resolve_defaults()` wrapper. |
|
|
29
|
+
| **Duplicated resolution** | `resolve_defaults()` is called 3 times (in `pull`, `check`, `_merge_multiple_upstreams`) with the same args. Each call independently resolves the same values. |
|
|
30
|
+
| **Bloated `resolve_defaults()`** | This function's original purpose was URL parameter defaults (branch, path, exclude). Boolean flags have nothing to do with URL resolution—they were bolted on. |
|
|
31
|
+
| **6-tuple destructuring** | `(branch, path, exclude, validate, strict, pre_commit) = resolve_defaults(...)` is fragile and unreadable. Adding one more field breaks every call site. |
|
|
32
|
+
| **Two representations, unclear boundary** | `Arguments` now holds `MISSING` values, but the CLI layer already resolved them in `_resolve_validate()` / `_resolve_strict()`. So `MISSING` can appear in `Arguments` from two sources: (a) the CLI resolver returned it, or (b) direct construction in tests. The semantics are unclear. |
|
|
33
|
+
|
|
34
|
+
### The real requirement
|
|
35
|
+
|
|
36
|
+
The `MISSING` sentinel is needed for **serialization only** (`serialize_ruff_sync_config`): when
|
|
37
|
+
`--save` is used, we must distinguish "user explicitly passed `--no-strict`" (serialize
|
|
38
|
+
`strict = false`) from "user said nothing about strict" (don't serialize the key at all). Execution
|
|
39
|
+
logic (`pull`, `check`) just needs a plain `bool`.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Proposed Design
|
|
44
|
+
|
|
45
|
+
**Core idea**: Separate the _transport/serialization_ concern (`MISSING` awareness) from the
|
|
46
|
+
_execution_ concern (plain bools).
|
|
47
|
+
|
|
48
|
+
### Layer 1: `Arguments` (transport + serialization)
|
|
49
|
+
|
|
50
|
+
Keep `bool | MissingType` for `validate`, `strict`, and `pre_commit` here. This is the type that
|
|
51
|
+
`serialize_ruff_sync_config()` consumes. It is constructed _once_ in `main()` and passed to
|
|
52
|
+
`serialize_ruff_sync_config()` via `pull()`.
|
|
53
|
+
|
|
54
|
+
### Layer 2: `ExecutionArgs` (new NamedTuple — execution logic)
|
|
55
|
+
|
|
56
|
+
A **new** NamedTuple with **only plain `bool`** for `validate`, `strict`, and `pre_commit`. This is
|
|
57
|
+
what `pull()` and `check()` (and `_merge_multiple_upstreams`) actually use for control flow. It is
|
|
58
|
+
derived from `Arguments` via a single call to a new `Arguments.resolve()` method (or a standalone
|
|
59
|
+
`resolve_execution_args()` function).
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
CLI (argparse) ──► _resolve_args() ──► main() builds Arguments
|
|
63
|
+
│
|
|
64
|
+
┌───────────────────┤
|
|
65
|
+
▼ ▼
|
|
66
|
+
serialize_ruff_sync_config Arguments.resolve()
|
|
67
|
+
(reads MISSING to decide ──► ExecutionArgs
|
|
68
|
+
what to serialize) (plain bools)
|
|
69
|
+
│
|
|
70
|
+
┌──────────┼──────────┐
|
|
71
|
+
▼ ▼ ▼
|
|
72
|
+
pull() check() _merge_multiple_upstreams()
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Step-by-Step Implementation
|
|
78
|
+
|
|
79
|
+
### Step 1: Create `ExecutionArgs` NamedTuple
|
|
80
|
+
|
|
81
|
+
**File**: `src/ruff_sync/cli.py`
|
|
82
|
+
|
|
83
|
+
Add a new NamedTuple right after the existing `Arguments` class:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
class ExecutionArgs(NamedTuple):
|
|
87
|
+
"""Resolved arguments for execution logic — all sentinels replaced with concrete values."""
|
|
88
|
+
|
|
89
|
+
command: str
|
|
90
|
+
upstream: tuple[URL, ...]
|
|
91
|
+
to: pathlib.Path
|
|
92
|
+
exclude: Iterable[str]
|
|
93
|
+
verbose: int
|
|
94
|
+
branch: str
|
|
95
|
+
path: str | None
|
|
96
|
+
semantic: bool
|
|
97
|
+
diff: bool
|
|
98
|
+
init: bool
|
|
99
|
+
pre_commit: bool # plain bool — MISSING resolved to default
|
|
100
|
+
save: bool | None
|
|
101
|
+
output_format: OutputFormat
|
|
102
|
+
validate: bool # plain bool — MISSING resolved to default
|
|
103
|
+
strict: bool # plain bool — MISSING resolved to default
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
> [!NOTE]
|
|
107
|
+
> `save` stays `bool | None` because `None` has meaningful semantics there (trigger
|
|
108
|
+
> from `--init`), and it's already resolved before execution.
|
|
109
|
+
|
|
110
|
+
### Step 2: Add a `resolve()` method to `Arguments`
|
|
111
|
+
|
|
112
|
+
**File**: `src/ruff_sync/cli.py`
|
|
113
|
+
|
|
114
|
+
Add a method to `Arguments` that produces an `ExecutionArgs`:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
class Arguments(NamedTuple):
|
|
118
|
+
# ... existing fields unchanged ...
|
|
119
|
+
|
|
120
|
+
def resolve(self) -> ExecutionArgs:
|
|
121
|
+
"""Resolve all MISSING sentinels to their effective defaults for execution."""
|
|
122
|
+
_, _, _, eff_validate, eff_strict, eff_pre_commit = resolve_defaults(
|
|
123
|
+
MISSING, # branch — already resolved, pass MISSING to skip
|
|
124
|
+
MISSING, # path — already resolved, pass MISSING to skip
|
|
125
|
+
MISSING, # exclude — already resolved, pass MISSING to skip
|
|
126
|
+
self.validate,
|
|
127
|
+
self.strict,
|
|
128
|
+
self.pre_commit,
|
|
129
|
+
)
|
|
130
|
+
return ExecutionArgs(
|
|
131
|
+
command=self.command,
|
|
132
|
+
upstream=self.upstream,
|
|
133
|
+
to=self.to,
|
|
134
|
+
exclude=self.exclude,
|
|
135
|
+
verbose=self.verbose,
|
|
136
|
+
branch=self.branch,
|
|
137
|
+
path=self.path,
|
|
138
|
+
semantic=self.semantic,
|
|
139
|
+
diff=self.diff,
|
|
140
|
+
init=self.init,
|
|
141
|
+
pre_commit=eff_pre_commit,
|
|
142
|
+
save=self.save,
|
|
143
|
+
output_format=self.output_format,
|
|
144
|
+
validate=eff_validate,
|
|
145
|
+
strict=eff_strict,
|
|
146
|
+
)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
> [!IMPORTANT]
|
|
150
|
+
> We still use `resolve_defaults()` for the boolean resolution logic (strict implies
|
|
151
|
+
> validate, defaults), but only call it **once** and only for the boolean fields.
|
|
152
|
+
|
|
153
|
+
### Step 3: Restore `resolve_defaults()` to its original scope
|
|
154
|
+
|
|
155
|
+
**File**: `src/ruff_sync/constants.py`
|
|
156
|
+
|
|
157
|
+
Revert `resolve_defaults()` to its original 3-parameter, 3-return form for URL parameter defaults.
|
|
158
|
+
Extract boolean resolution into a separate function:
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
def resolve_defaults(
|
|
162
|
+
branch: str | MissingType,
|
|
163
|
+
path: str | None | MissingType,
|
|
164
|
+
exclude: Iterable[str] | MissingType,
|
|
165
|
+
) -> tuple[str, str | None, Iterable[str]]:
|
|
166
|
+
"""Resolve MISSING sentinel values to their effective defaults.
|
|
167
|
+
|
|
168
|
+
This is the single source of truth for MISSING → default resolution for
|
|
169
|
+
URL-related parameters (branch, path, exclude).
|
|
170
|
+
"""
|
|
171
|
+
eff_branch = branch if branch is not MISSING else DEFAULT_BRANCH
|
|
172
|
+
raw_path = path if path is not MISSING else DEFAULT_PATH
|
|
173
|
+
eff_path: str | None = raw_path or None
|
|
174
|
+
eff_exclude = exclude if exclude is not MISSING else DEFAULT_EXCLUDE
|
|
175
|
+
return eff_branch, eff_path, eff_exclude
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def resolve_bool_flags(
|
|
179
|
+
validate: bool | MissingType = MISSING,
|
|
180
|
+
strict: bool | MissingType = MISSING,
|
|
181
|
+
pre_commit: bool | MissingType = MISSING,
|
|
182
|
+
) -> tuple[bool, bool, bool]:
|
|
183
|
+
"""Resolve MISSING sentinel values for boolean execution flags.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
A ``(validate, strict, pre_commit)`` tuple with defaults applied.
|
|
187
|
+
``strict=True`` implicitly enables ``validate``.
|
|
188
|
+
"""
|
|
189
|
+
eff_strict = strict if strict is not MISSING else False
|
|
190
|
+
eff_validate = (validate if validate is not MISSING else False) or eff_strict
|
|
191
|
+
eff_pre_commit = pre_commit if pre_commit is not MISSING else True
|
|
192
|
+
return eff_validate, eff_strict, eff_pre_commit
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
> [!TIP]
|
|
196
|
+
> This is a clean separation: `resolve_defaults` handles URL stuff, `resolve_bool_flags`
|
|
197
|
+
> handles boolean stuff. Neither grows unbounded.
|
|
198
|
+
|
|
199
|
+
### Step 4: Update `Arguments.resolve()` to use `resolve_bool_flags`
|
|
200
|
+
|
|
201
|
+
**File**: `src/ruff_sync/cli.py`
|
|
202
|
+
|
|
203
|
+
Replace the `resolve_defaults()` call in `Arguments.resolve()` with `resolve_bool_flags()`:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
from ruff_sync.constants import resolve_bool_flags
|
|
207
|
+
|
|
208
|
+
class Arguments(NamedTuple):
|
|
209
|
+
# ...
|
|
210
|
+
def resolve(self) -> ExecutionArgs:
|
|
211
|
+
eff_validate, eff_strict, eff_pre_commit = resolve_bool_flags(
|
|
212
|
+
self.validate, self.strict, self.pre_commit,
|
|
213
|
+
)
|
|
214
|
+
return ExecutionArgs(
|
|
215
|
+
command=self.command,
|
|
216
|
+
upstream=self.upstream,
|
|
217
|
+
to=self.to,
|
|
218
|
+
exclude=self.exclude,
|
|
219
|
+
verbose=self.verbose,
|
|
220
|
+
branch=self.branch,
|
|
221
|
+
path=self.path,
|
|
222
|
+
semantic=self.semantic,
|
|
223
|
+
diff=self.diff,
|
|
224
|
+
init=self.init,
|
|
225
|
+
pre_commit=eff_pre_commit,
|
|
226
|
+
save=self.save,
|
|
227
|
+
output_format=self.output_format,
|
|
228
|
+
validate=eff_validate,
|
|
229
|
+
strict=eff_strict,
|
|
230
|
+
)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Step 5: Update `pull()` to use `ExecutionArgs`
|
|
234
|
+
|
|
235
|
+
**File**: `src/ruff_sync/core.py`
|
|
236
|
+
|
|
237
|
+
Remove the `resolve_defaults()` call at the top of `pull()`. Instead, accept
|
|
238
|
+
**both** `Arguments` (for serialization) and call `.resolve()` once at the top:
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
async def pull(args: Arguments) -> int:
|
|
242
|
+
exec_args = args.resolve() # single resolution point
|
|
243
|
+
|
|
244
|
+
# Use exec.validate, exec.strict, exec.pre_commit for logic
|
|
245
|
+
# Use args (original, with MISSING) only for serialize_ruff_sync_config()
|
|
246
|
+
|
|
247
|
+
...
|
|
248
|
+
if exec.validate:
|
|
249
|
+
...validate_merged_config(..., strict=exec.strict, exclude=exec.exclude)
|
|
250
|
+
|
|
251
|
+
...
|
|
252
|
+
if should_save:
|
|
253
|
+
serialize_ruff_sync_config(source_doc, args) # ← still uses raw args
|
|
254
|
+
|
|
255
|
+
...
|
|
256
|
+
if exec.pre_commit:
|
|
257
|
+
sync_pre_commit(...)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
> [!WARNING]
|
|
261
|
+
> `serialize_ruff_sync_config()` **must** still receive the original `Arguments` (with
|
|
262
|
+
> `MISSING`) so it knows which flags to write and which to omit. Do NOT pass `ExecutionArgs` here.
|
|
263
|
+
|
|
264
|
+
### Step 6: Update `check()` to use `ExecutionArgs`
|
|
265
|
+
|
|
266
|
+
**File**: `src/ruff_sync/core.py`
|
|
267
|
+
|
|
268
|
+
Same pattern: call `args.resolve()` once, use the result for execution:
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
async def check(args: Arguments) -> int:
|
|
272
|
+
exec_args = args.resolve()
|
|
273
|
+
|
|
274
|
+
...
|
|
275
|
+
exit_code = _check_pre_commit_sync(exec.pre_commit, fmt)
|
|
276
|
+
...
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Remove the `resolve_defaults()` call and the 6-tuple destructuring.
|
|
280
|
+
|
|
281
|
+
### Step 7: Update `_merge_multiple_upstreams()`
|
|
282
|
+
|
|
283
|
+
**File**: `src/ruff_sync/core.py`
|
|
284
|
+
|
|
285
|
+
This function currently calls `resolve_defaults()` to get `branch`, `path`, and `exclude`. Since
|
|
286
|
+
these are already resolved by the time they reach `Arguments` (the CLI layer resolves them), the
|
|
287
|
+
call is actually redundant. However, if the function is also used by code that passes un-resolved
|
|
288
|
+
`Arguments`, we should change its signature to accept `ExecutionArgs` instead:
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
async def _merge_multiple_upstreams(
|
|
292
|
+
target_doc: TOMLDocument,
|
|
293
|
+
is_target_ruff_toml: bool,
|
|
294
|
+
args: ExecutionArgs, # ← changed from Arguments
|
|
295
|
+
client: httpx.AsyncClient,
|
|
296
|
+
) -> TOMLDocument:
|
|
297
|
+
# No resolve_defaults() needed — args already has plain values
|
|
298
|
+
fetch_results = await fetch_upstreams_concurrently(
|
|
299
|
+
args.upstream, client, branch=args.branch, path=args.path
|
|
300
|
+
)
|
|
301
|
+
...
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
> [!NOTE]
|
|
305
|
+
> Look at call sites: `_merge_multiple_upstreams` is called from both `pull()` and `check()`.
|
|
306
|
+
> Both will now pass `exec` (the `ExecutionArgs`) instead of raw `args`.
|
|
307
|
+
|
|
308
|
+
### Step 8: Remove `resolve_defaults` import from `core.py`
|
|
309
|
+
|
|
310
|
+
**File**: `src/ruff_sync/core.py`
|
|
311
|
+
|
|
312
|
+
After steps 5–7, `core.py` no longer needs `resolve_defaults` or `MISSING` from constants (unless
|
|
313
|
+
other code in the module still uses them). Clean up the imports.
|
|
314
|
+
|
|
315
|
+
### Step 9: Clean up `ResolvedArgs`
|
|
316
|
+
|
|
317
|
+
**File**: `src/ruff_sync/cli.py`
|
|
318
|
+
|
|
319
|
+
The `ResolvedArgs` NamedTuple was expanded to include `validate: bool | MissingType`,
|
|
320
|
+
`strict: bool | MissingType`, and `pre_commit: bool | MissingType`. These should be reverted to
|
|
321
|
+
exclude the boolean flags. `ResolvedArgs` is only used inside `_resolve_args()` → `main()`, and
|
|
322
|
+
the boolean flags are already individually resolved by `_resolve_validate()` and `_resolve_strict()`.
|
|
323
|
+
|
|
324
|
+
Two options:
|
|
325
|
+
|
|
326
|
+
- **Option A (recommended)**: Remove `validate`, `strict`, `pre_commit` from `ResolvedArgs`.
|
|
327
|
+
Put them back onto `Arguments` directly in `main()` using the existing `_resolve_*` functions
|
|
328
|
+
(which already return `bool | MissingType`).
|
|
329
|
+
|
|
330
|
+
- **Option B**: Leave `ResolvedArgs` as-is but document that its purpose is to shuttle values
|
|
331
|
+
between `_resolve_args()` and `main()`.
|
|
332
|
+
|
|
333
|
+
With **Option A**, `main()` becomes:
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
resolved = _resolve_args(args, config, initial_to) # no validate/strict/pre_commit
|
|
337
|
+
|
|
338
|
+
exec_args = Arguments(
|
|
339
|
+
...
|
|
340
|
+
validate=_resolve_validate(args, config),
|
|
341
|
+
strict=_resolve_strict(args, config),
|
|
342
|
+
pre_commit=_resolve_pre_commit(args, config),
|
|
343
|
+
...
|
|
344
|
+
)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Step 10: Update tests
|
|
348
|
+
|
|
349
|
+
**File**: `tests/test_constants.py`
|
|
350
|
+
|
|
351
|
+
Revert `resolve_defaults` tests to the original 3-tuple assertions:
|
|
352
|
+
|
|
353
|
+
```python
|
|
354
|
+
def test_resolve_defaults_all_missing():
|
|
355
|
+
branch, path, exclude = resolve_defaults(MISSING, MISSING, MISSING)
|
|
356
|
+
assert branch == DEFAULT_BRANCH
|
|
357
|
+
assert path is None
|
|
358
|
+
assert exclude == DEFAULT_EXCLUDE
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Add **new** tests for `resolve_bool_flags()`:
|
|
362
|
+
|
|
363
|
+
```python
|
|
364
|
+
from ruff_sync.constants import resolve_bool_flags
|
|
365
|
+
|
|
366
|
+
def test_resolve_bool_flags_all_missing():
|
|
367
|
+
validate, strict, pre_commit = resolve_bool_flags(MISSING, MISSING, MISSING)
|
|
368
|
+
assert validate is False
|
|
369
|
+
assert strict is False
|
|
370
|
+
assert pre_commit is True
|
|
371
|
+
|
|
372
|
+
def test_resolve_bool_flags_strict_implies_validate():
|
|
373
|
+
validate, strict, pre_commit = resolve_bool_flags(MISSING, True, MISSING)
|
|
374
|
+
assert validate is True
|
|
375
|
+
assert strict is True
|
|
376
|
+
|
|
377
|
+
def test_resolve_bool_flags_explicit_false():
|
|
378
|
+
validate, strict, pre_commit = resolve_bool_flags(False, False, False)
|
|
379
|
+
assert validate is False
|
|
380
|
+
assert strict is False
|
|
381
|
+
assert pre_commit is False
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**File**: `tests/test_serialization.py`
|
|
385
|
+
|
|
386
|
+
No changes needed — these tests construct `Arguments` with `MISSING` and pass to
|
|
387
|
+
`serialize_ruff_sync_config()`, which still consumes raw `Arguments`.
|
|
388
|
+
|
|
389
|
+
**File**: `tests/test_config_validation.py`
|
|
390
|
+
|
|
391
|
+
Update tests that currently destructure 6-tuples from `resolve_defaults()` to use the new API. Tests
|
|
392
|
+
for `pull()` and `check()` already pass `Arguments` objects, so they should work unchanged.
|
|
393
|
+
|
|
394
|
+
### Step 11: Run full validation
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
uv run ruff check . --fix
|
|
398
|
+
uv run ruff format .
|
|
399
|
+
uv run mypy .
|
|
400
|
+
uv run pytest -vv
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Summary of Changes
|
|
406
|
+
|
|
407
|
+
| File | Change |
|
|
408
|
+
|------|--------|
|
|
409
|
+
| `src/ruff_sync/constants.py` | Revert `resolve_defaults` to 3-param/3-return. Add `resolve_bool_flags()`. |
|
|
410
|
+
| `src/ruff_sync/cli.py` | Add `ExecutionArgs` NamedTuple. Add `Arguments.resolve()` method. Clean up `ResolvedArgs`. |
|
|
411
|
+
| `src/ruff_sync/core.py` | Replace `resolve_defaults()` calls in `pull`/`check`/`_merge_multiple_upstreams` with `args.resolve()`. Remove `MISSING`/`resolve_defaults` imports. |
|
|
412
|
+
| `tests/test_constants.py` | Revert to 3-tuple tests. Add `resolve_bool_flags` tests. |
|
|
413
|
+
| `tests/test_config_validation.py` | Minor updates if any test directly calls `resolve_defaults` with 6 args. |
|
|
414
|
+
|
|
415
|
+
### What does NOT change
|
|
416
|
+
|
|
417
|
+
- `Arguments` field types stay `bool | MissingType` for `validate`, `strict`, `pre_commit` (needed for serialization).
|
|
418
|
+
- `serialize_ruff_sync_config()` is unchanged — it still reads `MISSING` from `Arguments`.
|
|
419
|
+
- `_resolve_validate()`, `_resolve_strict()`, `_resolve_pre_commit()` in `cli.py` are unchanged.
|
|
420
|
+
- CLI argument parser is unchanged.
|
|
421
|
+
|
|
422
|
+
### Architecture benefit
|
|
423
|
+
|
|
424
|
+
```
|
|
425
|
+
Before (051ca28):
|
|
426
|
+
Arguments (MISSING everywhere) ──► resolve_defaults() called 3× in core.py
|
|
427
|
+
|
|
428
|
+
After (this refactor):
|
|
429
|
+
Arguments (MISSING for serialize) ──► .resolve() ──► ExecutionArgs (plain bools) ──► core logic
|
|
430
|
+
↑ called ONCE
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
The sentinel is contained to the serialization boundary. Execution logic gets clean types.
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## Risks & Mitigations
|
|
438
|
+
|
|
439
|
+
| Risk | Mitigation |
|
|
440
|
+
|------|------------|
|
|
441
|
+
| Changing `_merge_multiple_upstreams` signature breaks internal callers | It's a private function with only 2 call sites (`pull`, `check`). Both are updated in the same PR. |
|
|
442
|
+
| `ExecutionArgs` adds another NamedTuple | It replaces the ad-hoc 6-tuple destructuring, which is worse. The NamedTuple is self-documenting and type-safe. |
|
|
443
|
+
| Tests that construct `Arguments` directly need `MISSING` for new defaults | Already the case — no change. |
|