goal 2.2.0__tar.gz → 2.2.2__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.
- {goal-2.2.0 → goal-2.2.2}/PKG-INFO +25 -2
- {goal-2.2.0 → goal-2.2.2}/README.md +24 -0
- {goal-2.2.0 → goal-2.2.2}/goal/__init__.py +1 -1
- {goal-2.2.0 → goal-2.2.2}/goal/bootstrap/costs_badge.py +12 -8
- {goal-2.2.0 → goal-2.2.2}/goal/bootstrap/installer.py +12 -16
- {goal-2.2.0 → goal-2.2.2}/goal/bootstrap/pyproject_costs_setup.py +3 -2
- {goal-2.2.0 → goal-2.2.2}/goal/changelog.py +7 -2
- {goal-2.2.0 → goal-2.2.2}/goal/cli/__init__.py +44 -5
- {goal-2.2.0 → goal-2.2.2}/goal/cli/governance_cmd.py +104 -3
- {goal-2.2.0 → goal-2.2.2}/goal/cli/publish.py +89 -13
- {goal-2.2.0 → goal-2.2.2}/goal/cli/push_cmd.py +10 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/tests.py +82 -7
- {goal-2.2.0 → goal-2.2.2}/goal/cli/tests_discovery.py +10 -10
- {goal-2.2.0 → goal-2.2.2}/goal/cli/tests_pytest_setup.py +44 -34
- goal-2.2.2/goal/cli/version_discovery.py +29 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/version_state.py +17 -12
- {goal-2.2.0 → goal-2.2.2}/goal/cli/version_sync.py +25 -25
- {goal-2.2.0 → goal-2.2.2}/goal/cli/version_utils.py +52 -12
- {goal-2.2.0 → goal-2.2.2}/goal/config/manager.py +18 -2
- {goal-2.2.0 → goal-2.2.2}/goal/deep_analyzer_aggregate.py +7 -5
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/rust.py +15 -7
- {goal-2.2.0 → goal-2.2.2}/goal/generator/analyzer.py +28 -17
- {goal-2.2.0 → goal-2.2.2}/goal/generator/generator.py +10 -8
- {goal-2.2.0 → goal-2.2.2}/goal/git_ops.py +5 -1
- goal-2.2.2/goal/governance/adoption_plan.py +214 -0
- goal-2.2.2/goal/governance/adoption_transaction.py +478 -0
- goal-2.2.2/goal/governance/branch_intent.py +106 -0
- {goal-2.2.0 → goal-2.2.2}/goal/governance/delivery.py +602 -85
- goal-2.2.2/goal/governance/remediation.py +256 -0
- {goal-2.2.0 → goal-2.2.2}/goal/postcommit/manager.py +4 -1
- {goal-2.2.0 → goal-2.2.2}/goal/project_bootstrap.py +74 -298
- {goal-2.2.0 → goal-2.2.2}/goal/push/core.py +178 -40
- {goal-2.2.0 → goal-2.2.2}/goal/push/stages/commit.py +16 -7
- {goal-2.2.0 → goal-2.2.2}/goal/push/stages/publish.py +14 -1
- {goal-2.2.0 → goal-2.2.2}/goal/recovery/corrupted.py +9 -6
- {goal-2.2.0 → goal-2.2.2}/goal/recovery/large_file.py +4 -2
- {goal-2.2.0 → goal-2.2.2}/goal/recovery/manager.py +15 -11
- goal-2.2.2/goal/rust_project.py +45 -0
- {goal-2.2.0 → goal-2.2.2}/goal/summary/generator.py +25 -2
- {goal-2.2.0 → goal-2.2.2}/goal.egg-info/PKG-INFO +25 -2
- {goal-2.2.0 → goal-2.2.2}/goal.egg-info/SOURCES.txt +18 -0
- {goal-2.2.0 → goal-2.2.2}/goal.egg-info/requires.txt +0 -3
- {goal-2.2.0 → goal-2.2.2}/pyproject.toml +3 -26
- goal-2.2.2/tests/test_adoption_plan.py +229 -0
- goal-2.2.2/tests/test_adoption_transaction.py +809 -0
- goal-2.2.2/tests/test_branch_intent.py +173 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_cli_options.py +30 -0
- goal-2.2.2/tests/test_commit_generator.py +48 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_delivery_integrity.py +134 -3
- {goal-2.2.0 → goal-2.2.2}/tests/test_governance_cmd.py +103 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_governance_delivery.py +399 -5
- goal-2.2.2/tests/test_governance_remediation.py +177 -0
- goal-2.2.2/tests/test_governance_source_hub.py +315 -0
- goal-2.2.2/tests/test_governed_cli_bootstrap.py +96 -0
- goal-2.2.2/tests/test_nested_test_routing.py +247 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_project_bootstrap.py +119 -113
- {goal-2.2.0 → goal-2.2.2}/tests/test_project_doctor.py +34 -11
- {goal-2.2.0 → goal-2.2.2}/tests/test_publish_changes.py +62 -0
- goal-2.2.2/tests/test_publish_python.py +58 -0
- goal-2.2.2/tests/test_publish_stage.py +71 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_push_core_refactor.py +41 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_push_e2e.py +3 -1
- goal-2.2.2/tests/test_push_workflow_completion.py +308 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_pyproject_costs_setup.py +6 -4
- goal-2.2.2/tests/test_release_only_delivery.py +65 -0
- goal-2.2.2/tests/test_version_repository_boundaries.py +147 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_version_state.py +30 -0
- goal-2.2.0/tests/test_governance_source_hub.py +0 -121
- {goal-2.2.0 → goal-2.2.2}/LICENSE +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/__main__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/authors/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/authors/manager.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/authors/utils.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/bootstrap/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/bootstrap/configurator.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/bootstrap/detector.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/bootstrap/templates.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/all_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/authors_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/commit_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/config_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/config_validate_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/dependencies_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/doctor_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/hooks_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/license_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/postcommit_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/publish_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/recover_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/utils_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/validation_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/version.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/version_types.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli/wizard_cmd.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/cli_helpers.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/commit_generator.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/config/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/config/constants.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/config/validation.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/config.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/deep_analyzer.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/deep_analyzer_patterns.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/dependency_update.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/core.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/dotnet.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/go.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/java.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/logging.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/models.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/nodejs.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/php.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/python.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/python_diag_core.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/python_diag_extended.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/ruby.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/doctor/todo.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/enhanced_summary.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/formatter.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/generator/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/generator/git_ops.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/governance/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/hooks/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/hooks/config.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/hooks/manager.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/installers/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/installers/broker.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/installers/config.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/installers/env.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/installers/managers/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/installers/managers/base.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/installers/managers/pdm.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/installers/managers/pip.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/installers/managers/poetry.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/installers/managers/uv.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/internal_dependencies.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/io/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/io/stdio.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/license/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/license/manager.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/license/spdx.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/package_managers.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/postcommit/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/postcommit/actions.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/project_doctor.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/publish/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/publish/changes.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/publish/github_fallback.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/push/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/push/commands.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/push/preview.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/push/stages/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/push/stages/changelog.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/push/stages/costs.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/push/stages/dry_run.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/push/stages/push_remote.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/push/stages/tag.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/push/stages/test.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/push/stages/todo.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/push/stages/version.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/push/tickets.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/pyenv_health.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/recovery/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/recovery/auth.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/recovery/base.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/recovery/divergent.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/recovery/exceptions.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/recovery/force_push.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/recovery/lfs.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/recovery/strategies.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/smart_commit/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/smart_commit/abstraction.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/smart_commit/generator.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/smart_commit/generator_core.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/smart_commit/generator_generate.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/smart_commit.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/summary/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/summary/body_formatter.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/summary/quality_filter.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/summary/validator.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/toml_validation.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/user_config.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/validation/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/validation/manager.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/validation/rules.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/validators/__init__.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/validators/dot_folders.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/validators/exceptions.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/validators/file_validator.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/validators/gitignore.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/validators/tokens.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal/version_validation.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal.egg-info/dependency_links.txt +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal.egg-info/entry_points.txt +0 -0
- {goal-2.2.0 → goal-2.2.2}/goal.egg-info/top_level.txt +0 -0
- {goal-2.2.0 → goal-2.2.2}/setup.cfg +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_bootstrap_installer_python_test_dependency.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_changelog.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_cli_tests_runner.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_clone_repo.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_committed_unreleased.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_config_shim.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_config_validation.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_cost_badges.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_dependency_update.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_detect_version_files.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_dev_install.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_file_validation.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_formatter.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_git_ops.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_github_fallback.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_gitignore_validator.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_installer_env.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_installers_e2e.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_internal_dependencies.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_package_managers.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_project_bootstrap_costs.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_publish_pattern.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_push_retry.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_push_todo_stage.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_pyenv_health.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_recovery_preserves_work.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_smart_commit_shim.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_token_validator_patterns.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_user_config.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_version_sync.py +0 -0
- {goal-2.2.0 → goal-2.2.2}/tests/test_version_validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: goal
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: Goal - Automated git push with enterprise-grade commit intelligence, smart conventional commit generation based on deep code analysis, and interactive release workflow management.
|
|
5
5
|
Author-email: Tom Sapletta <tom@sapletta.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -25,7 +25,6 @@ Provides-Extra: dev
|
|
|
25
25
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
26
26
|
Requires-Dist: build; extra == "dev"
|
|
27
27
|
Requires-Dist: twine; extra == "dev"
|
|
28
|
-
Requires-Dist: pfix>=0.1.60; python_version >= "3.10" and extra == "dev"
|
|
29
28
|
Requires-Dist: tox>=4.0.0; extra == "dev"
|
|
30
29
|
Dynamic: license-file
|
|
31
30
|
|
|
@@ -112,6 +111,25 @@ workflow and remaining project-local Docker/ticket steps.
|
|
|
112
111
|
|
|
113
112
|
### ✨ New Features
|
|
114
113
|
|
|
114
|
+
**🔄 Autonomous publication completion (governed repositories)**
|
|
115
|
+
- After the protected merge lands, a governed `goal -a` on a clean,
|
|
116
|
+
synchronized base with committed-but-unpublished package sources now
|
|
117
|
+
**completes the waited-for publication** automatically: it records a
|
|
118
|
+
`publish-completion` delivery event, re-enables publishing and finishes
|
|
119
|
+
tag + registry upload without creating a new commit.
|
|
120
|
+
- Without pending sources the boundary stays a no-change return — clean main
|
|
121
|
+
remains a safe, quiet no-op.
|
|
122
|
+
|
|
123
|
+
**🛡️ Frozen goal.yaml protection**
|
|
124
|
+
- Version-file auto-detection now skips tooling caches and governed runtime
|
|
125
|
+
state (`.pytest_cache`, `.subactor`, `.worktrees`, …), so stale snapshots
|
|
126
|
+
can never be learned as version carriers.
|
|
127
|
+
- An explicit, non-empty `versioning.files` list is authoritative: detection
|
|
128
|
+
is bootstrap-only and never replaces it.
|
|
129
|
+
- Governed targets load the frozen `goal.yaml` from **every** command
|
|
130
|
+
(`goal publish` included) — auto-detection can no longer rewrite a
|
|
131
|
+
protected configuration as a side effect.
|
|
132
|
+
|
|
115
133
|
**🧹 `goal all` — sweep many repos at once**
|
|
116
134
|
- `goal all [PATHS...]` runs `goal -a` in every sub-repo that has uncommitted
|
|
117
135
|
changes (defaults to `*` — all sub-folders of the current directory).
|
|
@@ -1859,3 +1877,8 @@ _Last updated by [taskill](https://github.com/oqlos/taskill) at 2026-04-25 13:38
|
|
|
1859
1877
|
> Introduces a configuration management system, improves the code analysis engine and commit message generation, expands CLI functionality and markdown output support, and applies bulk TODO fixes and documentation updates.
|
|
1860
1878
|
|
|
1861
1879
|
<!-- taskill:status:end -->
|
|
1880
|
+
|
|
1881
|
+
|
|
1882
|
+
## Documentation ownership
|
|
1883
|
+
|
|
1884
|
+
Before producing a report or plan, follow [documentation instructions](docs/AGENTS.md) and use the [documentation index](docs/README.md). Durable results belong in versioned repository documentation; private recovery archives and raw logs stay outside publication.
|
|
@@ -81,6 +81,25 @@ workflow and remaining project-local Docker/ticket steps.
|
|
|
81
81
|
|
|
82
82
|
### ✨ New Features
|
|
83
83
|
|
|
84
|
+
**🔄 Autonomous publication completion (governed repositories)**
|
|
85
|
+
- After the protected merge lands, a governed `goal -a` on a clean,
|
|
86
|
+
synchronized base with committed-but-unpublished package sources now
|
|
87
|
+
**completes the waited-for publication** automatically: it records a
|
|
88
|
+
`publish-completion` delivery event, re-enables publishing and finishes
|
|
89
|
+
tag + registry upload without creating a new commit.
|
|
90
|
+
- Without pending sources the boundary stays a no-change return — clean main
|
|
91
|
+
remains a safe, quiet no-op.
|
|
92
|
+
|
|
93
|
+
**🛡️ Frozen goal.yaml protection**
|
|
94
|
+
- Version-file auto-detection now skips tooling caches and governed runtime
|
|
95
|
+
state (`.pytest_cache`, `.subactor`, `.worktrees`, …), so stale snapshots
|
|
96
|
+
can never be learned as version carriers.
|
|
97
|
+
- An explicit, non-empty `versioning.files` list is authoritative: detection
|
|
98
|
+
is bootstrap-only and never replaces it.
|
|
99
|
+
- Governed targets load the frozen `goal.yaml` from **every** command
|
|
100
|
+
(`goal publish` included) — auto-detection can no longer rewrite a
|
|
101
|
+
protected configuration as a side effect.
|
|
102
|
+
|
|
84
103
|
**🧹 `goal all` — sweep many repos at once**
|
|
85
104
|
- `goal all [PATHS...]` runs `goal -a` in every sub-repo that has uncommitted
|
|
86
105
|
changes (defaults to `*` — all sub-folders of the current directory).
|
|
@@ -1828,3 +1847,8 @@ _Last updated by [taskill](https://github.com/oqlos/taskill) at 2026-04-25 13:38
|
|
|
1828
1847
|
> Introduces a configuration management system, improves the code analysis engine and commit message generation, expands CLI functionality and markdown output support, and applies bulk TODO fixes and documentation updates.
|
|
1829
1848
|
|
|
1830
1849
|
<!-- taskill:status:end -->
|
|
1850
|
+
|
|
1851
|
+
|
|
1852
|
+
## Documentation ownership
|
|
1853
|
+
|
|
1854
|
+
Before producing a report or plan, follow [documentation instructions](docs/AGENTS.md) and use the [documentation index](docs/README.md). Durable results belong in versioned repository documentation; private recovery archives and raw logs stay outside publication.
|
|
@@ -51,6 +51,16 @@ def _costs_version_satisfies_minimum(version: object) -> bool:
|
|
|
51
51
|
return bool(current) and current >= minimum
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
def _run_costs_install_command(command: list[str], project_dir: Path) -> bool:
|
|
55
|
+
install_result = subprocess.run(
|
|
56
|
+
command,
|
|
57
|
+
capture_output=True,
|
|
58
|
+
text=True,
|
|
59
|
+
cwd=str(project_dir),
|
|
60
|
+
)
|
|
61
|
+
return install_result.returncode == 0
|
|
62
|
+
|
|
63
|
+
|
|
54
64
|
def _install_costs_requirement(project_dir: Path, python_bin: str) -> bool:
|
|
55
65
|
requirement = f"costs>={_MIN_COSTS_VERSION}"
|
|
56
66
|
commands = [
|
|
@@ -61,13 +71,7 @@ def _install_costs_requirement(project_dir: Path, python_bin: str) -> bool:
|
|
|
61
71
|
commands.append([uv_bin, "pip", "install", "--python", python_bin, requirement])
|
|
62
72
|
|
|
63
73
|
for command in commands:
|
|
64
|
-
|
|
65
|
-
command,
|
|
66
|
-
capture_output=True,
|
|
67
|
-
text=True,
|
|
68
|
-
cwd=str(project_dir),
|
|
69
|
-
)
|
|
70
|
-
if install_result.returncode == 0:
|
|
74
|
+
if _run_costs_install_command(command, project_dir):
|
|
71
75
|
return True
|
|
72
76
|
return False
|
|
73
77
|
|
|
@@ -202,7 +206,7 @@ def _calculate_ai_costs(repo_root: Path):
|
|
|
202
206
|
all_commits_data = parse_commits(
|
|
203
207
|
str(repo_root), max_count=500, ai_only=False, full_history=True
|
|
204
208
|
)
|
|
205
|
-
ai_commits = _filter_ai_commits(all_commits_data)
|
|
209
|
+
ai_commits = _filter_ai_commits(all_commits_data, indicators=_AI_INDICATORS)
|
|
206
210
|
|
|
207
211
|
total_cost = sum(
|
|
208
212
|
_single_commit_ai_cost(
|
|
@@ -107,6 +107,16 @@ def _install_python_deps_legacy(
|
|
|
107
107
|
else cfg.get("dep_install_commands", [])
|
|
108
108
|
)
|
|
109
109
|
|
|
110
|
+
def _run_dep_install_cmd(command_str: str) -> subprocess.CompletedProcess:
|
|
111
|
+
return subprocess.run(
|
|
112
|
+
command_str,
|
|
113
|
+
shell=True,
|
|
114
|
+
cwd=str(project_dir),
|
|
115
|
+
capture_output=True,
|
|
116
|
+
text=True,
|
|
117
|
+
env=isolated_env(str(project_dir)),
|
|
118
|
+
)
|
|
119
|
+
|
|
110
120
|
for dep_cfg in dep_commands:
|
|
111
121
|
condition = dep_cfg["condition"]
|
|
112
122
|
if not _match_marker(project_dir, condition):
|
|
@@ -121,28 +131,14 @@ def _install_python_deps_legacy(
|
|
|
121
131
|
cmd = f"uv pip install {cmd.split('install ', 1)[1]}"
|
|
122
132
|
|
|
123
133
|
click.echo(click.style(f" Installing deps: {cmd}", fg="cyan"))
|
|
124
|
-
result =
|
|
125
|
-
cmd,
|
|
126
|
-
shell=True,
|
|
127
|
-
cwd=str(project_dir),
|
|
128
|
-
capture_output=True,
|
|
129
|
-
text=True,
|
|
130
|
-
env=isolated_env(str(project_dir)),
|
|
131
|
-
)
|
|
134
|
+
result = _run_dep_install_cmd(cmd)
|
|
132
135
|
|
|
133
136
|
if result.returncode != 0:
|
|
134
137
|
fallback = dep_cfg.get("fallback")
|
|
135
138
|
if fallback:
|
|
136
139
|
cmd = fallback.format(python=python_bin)
|
|
137
140
|
click.echo(click.style(f" Retrying: {cmd}", fg="yellow"))
|
|
138
|
-
result =
|
|
139
|
-
cmd,
|
|
140
|
-
shell=True,
|
|
141
|
-
cwd=str(project_dir),
|
|
142
|
-
capture_output=True,
|
|
143
|
-
text=True,
|
|
144
|
-
env=isolated_env(str(project_dir)),
|
|
145
|
-
)
|
|
141
|
+
result = _run_dep_install_cmd(cmd)
|
|
146
142
|
|
|
147
143
|
if result.returncode != 0:
|
|
148
144
|
click.echo(
|
|
@@ -10,10 +10,11 @@ import click
|
|
|
10
10
|
|
|
11
11
|
logger = logging.getLogger(__name__)
|
|
12
12
|
|
|
13
|
+
# pfix is deliberately not injected: its automatic repairs overwrote development
|
|
14
|
+
# changes, so projects must opt in explicitly instead of receiving it by default.
|
|
13
15
|
_REQUIRED_DEV_DEPS = (
|
|
14
16
|
("goal", '"goal>=2.1.0; python_version >= \'3.12\'"'),
|
|
15
17
|
("costs", '"costs>=0.1.53; python_version >= \'3.9\'"'),
|
|
16
|
-
("pfix", '"pfix>=0.1.60; python_version >= \'3.10\'"'),
|
|
17
18
|
)
|
|
18
19
|
_LEGACY_DEV_DEP_REPLACEMENTS = tuple(
|
|
19
20
|
(spec.split(";", 1)[0] + '"', spec)
|
|
@@ -181,7 +182,7 @@ def _ensure_costs_config(project_dir: Path) -> bool:
|
|
|
181
182
|
pyproject.write_text(content, encoding="utf-8")
|
|
182
183
|
click.echo(
|
|
183
184
|
click.style(
|
|
184
|
-
" ✓ Added goal
|
|
185
|
+
" ✓ Added goal and costs to dev dependencies", fg="green"
|
|
185
186
|
)
|
|
186
187
|
)
|
|
187
188
|
|
|
@@ -134,10 +134,15 @@ def update_changelog(
|
|
|
134
134
|
(config or {}).get("git", {}).get("changelog", {}).get("group_by_domain", False)
|
|
135
135
|
)
|
|
136
136
|
|
|
137
|
+
effective_files = (
|
|
138
|
+
changelog_entry.get("files", files)
|
|
139
|
+
if isinstance(changelog_entry, dict)
|
|
140
|
+
else files
|
|
141
|
+
)
|
|
137
142
|
if use_domain_grouping and config:
|
|
138
|
-
entry = _build_domain_entry(version, date_str,
|
|
143
|
+
entry = _build_domain_entry(version, date_str, effective_files, config)
|
|
139
144
|
else:
|
|
140
|
-
entry = _build_simple_entry(version, date_str,
|
|
145
|
+
entry = _build_simple_entry(version, date_str, effective_files)
|
|
141
146
|
|
|
142
147
|
changelog_path.write_text(_insert_entry(existing_content, entry))
|
|
143
148
|
|
|
@@ -440,6 +440,21 @@ def _resolve_output_markdown(output_markdown: bool | None, all_flags: bool) -> b
|
|
|
440
440
|
return bool(all_flags)
|
|
441
441
|
|
|
442
442
|
|
|
443
|
+
def _governed_clone() -> bool:
|
|
444
|
+
"""Whether another checkout of this clone carries adopted governance."""
|
|
445
|
+
from goal.governance.delivery import governed_clone_evidence
|
|
446
|
+
|
|
447
|
+
return bool(governed_clone_evidence())
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def _is_governed_target() -> bool:
|
|
451
|
+
"""Whether the current checkout has a new-project governance manifest."""
|
|
452
|
+
from pathlib import Path
|
|
453
|
+
from goal.governance.delivery import is_new_project_manifest
|
|
454
|
+
|
|
455
|
+
return is_new_project_manifest(Path(".governance/manifest.json"))
|
|
456
|
+
|
|
457
|
+
|
|
443
458
|
def _configure_main_context(
|
|
444
459
|
ctx,
|
|
445
460
|
bump,
|
|
@@ -458,8 +473,10 @@ def _configure_main_context(
|
|
|
458
473
|
abstraction,
|
|
459
474
|
delivery_mode,
|
|
460
475
|
read_only_governance=False,
|
|
476
|
+
koru=False,
|
|
461
477
|
) -> None:
|
|
462
478
|
ctx.ensure_object(dict)
|
|
479
|
+
ctx.obj["koru"] = koru
|
|
463
480
|
ctx.obj["bump"] = bump
|
|
464
481
|
ctx.obj["version"] = target_version
|
|
465
482
|
ctx.obj["yes"] = yes or all_flags
|
|
@@ -467,7 +484,12 @@ def _configure_main_context(
|
|
|
467
484
|
ctx.obj["recursive"] = recursive
|
|
468
485
|
ctx.obj["interactive"] = interactive
|
|
469
486
|
ctx.obj["no_publish"] = no_publish
|
|
470
|
-
ctx.obj["
|
|
487
|
+
ctx.obj["force_publish_explicit"] = force_publish
|
|
488
|
+
# The full workflow is a release intent. Treat it like the explicit
|
|
489
|
+
# --force-publish flag so a clean tree with already-committed package
|
|
490
|
+
# changes still reaches the registry publisher. --no-publish remains an
|
|
491
|
+
# explicit opt-out and is enforced by the publish stage.
|
|
492
|
+
ctx.obj["force_publish"] = force_publish or all_flags
|
|
471
493
|
ctx.obj["todo"] = todo
|
|
472
494
|
ctx.obj["markdown"] = _resolve_output_markdown(markdown, all_flags)
|
|
473
495
|
ctx.obj["dry_run"] = dry_run
|
|
@@ -478,9 +500,16 @@ def _configure_main_context(
|
|
|
478
500
|
ctx.obj["config"] = load_config(config_path) if config_path else load_config()
|
|
479
501
|
elif config_path:
|
|
480
502
|
ctx.obj["config"] = load_config(config_path)
|
|
503
|
+
elif _is_governed_target() or _governed_clone():
|
|
504
|
+
# Governed targets never rewrite goal.yaml from any command: the
|
|
505
|
+
# protected gate freezes the file and auto-detection must neither
|
|
506
|
+
# precede a refusal nor corrupt a frozen projection after a merge.
|
|
507
|
+
# Previously only dry-run/--all runs were protected; ``goal publish``
|
|
508
|
+
# and other subcommands still triggered ensure_config() rewrites.
|
|
509
|
+
ctx.obj["config"] = load_config()
|
|
481
510
|
elif dry_run:
|
|
482
|
-
# Dry
|
|
483
|
-
#
|
|
511
|
+
# Dry runs must reach their gate/no-change check before any mutation.
|
|
512
|
+
# ensure_config can create or rewrite goal.yaml during auto-detection.
|
|
484
513
|
ctx.obj["config"] = load_config()
|
|
485
514
|
else:
|
|
486
515
|
ctx.obj["config"] = ensure_config()
|
|
@@ -559,6 +588,7 @@ class GoalGroup(click.Group):
|
|
|
559
588
|
# We must skip option *values* (e.g. 'major' in '--bump major') so
|
|
560
589
|
# they are not mistaken for subcommand names.
|
|
561
590
|
has_all_flag = _has_cli_flag(args, "a", "--all")
|
|
591
|
+
has_koru_flag = _has_cli_flag(args, "k", "--koru")
|
|
562
592
|
known_cmds = set(self.list_commands(ctx) or [])
|
|
563
593
|
# Options whose next token is a value (not a flag).
|
|
564
594
|
_VALUE_OPTIONS = {
|
|
@@ -614,7 +644,7 @@ class GoalGroup(click.Group):
|
|
|
614
644
|
|
|
615
645
|
has_subcommand = any(p in known_cmds for p in positionals)
|
|
616
646
|
|
|
617
|
-
if has_all_flag and not has_subcommand:
|
|
647
|
+
if (has_all_flag or has_koru_flag) and not has_subcommand:
|
|
618
648
|
if positionals:
|
|
619
649
|
# `goal -a ./*` (or explicit dirs) → sweep those paths across
|
|
620
650
|
# every dirty sub-repo via the `all` command.
|
|
@@ -624,7 +654,7 @@ class GoalGroup(click.Group):
|
|
|
624
654
|
elif auto_used:
|
|
625
655
|
args = opts + positionals
|
|
626
656
|
else:
|
|
627
|
-
# Bare `goal -a` → default to the single-repo push workflow.
|
|
657
|
+
# Bare `goal -a` or `goal -k` → default to the single-repo push workflow.
|
|
628
658
|
push_cmd = click.Group.get_command(self, ctx, "push")
|
|
629
659
|
if push_cmd is not None:
|
|
630
660
|
args = (opts + ["push"]) if auto_used else (args + ["push"])
|
|
@@ -649,6 +679,13 @@ class GoalGroup(click.Group):
|
|
|
649
679
|
is_flag=True,
|
|
650
680
|
help="Run full workflow (tests, push, publish)",
|
|
651
681
|
)
|
|
682
|
+
@click.option(
|
|
683
|
+
"--koru",
|
|
684
|
+
"-k",
|
|
685
|
+
"koru",
|
|
686
|
+
is_flag=True,
|
|
687
|
+
help="Run Koru auto-repair before workflow",
|
|
688
|
+
)
|
|
652
689
|
@click.option(
|
|
653
690
|
"--upgrade-deps",
|
|
654
691
|
"-u",
|
|
@@ -705,6 +742,7 @@ def main(
|
|
|
705
742
|
target_version,
|
|
706
743
|
yes,
|
|
707
744
|
all_flags,
|
|
745
|
+
koru,
|
|
708
746
|
upgrade_deps,
|
|
709
747
|
recursive,
|
|
710
748
|
interactive,
|
|
@@ -754,6 +792,7 @@ def main(
|
|
|
754
792
|
abstraction,
|
|
755
793
|
delivery_mode,
|
|
756
794
|
read_only_governance or is_help_request,
|
|
795
|
+
koru=koru,
|
|
757
796
|
)
|
|
758
797
|
|
|
759
798
|
|
|
@@ -502,6 +502,39 @@ def workspace_check(target_root, workspace_root, allow, output_format):
|
|
|
502
502
|
raise click.exceptions.Exit(result.returncode)
|
|
503
503
|
|
|
504
504
|
|
|
505
|
+
@governance.command("branch-intent-check")
|
|
506
|
+
@click.option("--target-root", default=".", show_default=True,
|
|
507
|
+
type=click.Path(exists=True, file_okay=False, path_type=Path))
|
|
508
|
+
@click.option("--report", required=True,
|
|
509
|
+
type=click.Path(exists=True, dir_okay=False, path_type=Path))
|
|
510
|
+
@click.option("--observation", required=True,
|
|
511
|
+
type=click.Path(exists=True, dir_okay=False, path_type=Path))
|
|
512
|
+
@click.option("--evidence-root", required=True,
|
|
513
|
+
type=click.Path(exists=True, file_okay=False, path_type=Path))
|
|
514
|
+
@click.option("--expected-lock-sha256", required=True,
|
|
515
|
+
help="Adoption lock digest supplied independently by the protected caller.")
|
|
516
|
+
@click.option("--timeout", default=30, show_default=True, type=click.IntRange(1, 300))
|
|
517
|
+
def branch_intent_check(target_root, report, observation, evidence_root,
|
|
518
|
+
expected_lock_sha256, timeout):
|
|
519
|
+
"""Check a preserved branch's intent report without deleting anything.
|
|
520
|
+
|
|
521
|
+
Requires a published adopted package containing the reconciliation checker.
|
|
522
|
+
The protected caller verifies observation provenance and criterion coverage;
|
|
523
|
+
do not derive the expected lock digest from an author's untrusted checkout.
|
|
524
|
+
Exit 0 means ready for owner review, 1 unresolved, and 2 invalid evidence.
|
|
525
|
+
Report readiness never grants discard, merge or ticket-close authority.
|
|
526
|
+
"""
|
|
527
|
+
from goal.governance.branch_intent import run_check
|
|
528
|
+
|
|
529
|
+
result = run_check(target_root, report, observation, evidence_root,
|
|
530
|
+
expected_lock_sha256, timeout)
|
|
531
|
+
click.echo(result.stdout, nl=False)
|
|
532
|
+
if result.stderr:
|
|
533
|
+
click.echo(result.stderr, err=True, nl=False)
|
|
534
|
+
if result.returncode:
|
|
535
|
+
raise click.exceptions.Exit(result.returncode)
|
|
536
|
+
|
|
537
|
+
|
|
505
538
|
@governance.group("delivery-hook")
|
|
506
539
|
def delivery_hook():
|
|
507
540
|
"""Manage the local fail-fast pre-push guard."""
|
|
@@ -568,6 +601,51 @@ def verify_delivery(ctx, delivery_mode):
|
|
|
568
601
|
click.echo(json.dumps(payload, indent=2, sort_keys=True))
|
|
569
602
|
|
|
570
603
|
|
|
604
|
+
@governance.command("plan-adoption")
|
|
605
|
+
@click.option("--catalog", required=True,
|
|
606
|
+
type=click.Path(exists=True, dir_okay=False, path_type=Path))
|
|
607
|
+
@click.option("--catalog-sha256", required=True,
|
|
608
|
+
help="Independently acquired SHA-256 of operator-supplied catalog bytes.")
|
|
609
|
+
@click.option("--target-root", default=".", show_default=True,
|
|
610
|
+
type=click.Path(exists=True, file_okay=False, path_type=Path))
|
|
611
|
+
@click.option("--target-revision", default=None,
|
|
612
|
+
help="Explicit destination SHA; otherwise preserve a supported current pin.")
|
|
613
|
+
def plan_adoption_command(catalog, catalog_sha256, target_root, target_revision):
|
|
614
|
+
"""Emit an offline advisory adoption plan; never fetch, write or call an LLM.
|
|
615
|
+
|
|
616
|
+
Catalog v1: schema=goal.adoption-catalog/v1,
|
|
617
|
+
standardRepository=wellmanifest/new-project, supportedRevisions=[SHA],
|
|
618
|
+
migrations=[{fromRevision: SHA, toRevision: SHA,
|
|
619
|
+
recipe: goal-governance-adopt/v1}]. All revisions are immutable commit SHAs.
|
|
620
|
+
|
|
621
|
+
Exit 0 means retain or migration planned, NOT policy approval or product
|
|
622
|
+
readiness. Exit 2 means blocked. No persistent queue or automatic upgrade
|
|
623
|
+
is created. Execute only through an independently authorized controller.
|
|
624
|
+
"""
|
|
625
|
+
from goal.governance.adoption_plan import AdoptionPlanError, plan_adoption
|
|
626
|
+
|
|
627
|
+
try:
|
|
628
|
+
plan = plan_adoption(target_root, catalog, catalog_sha256, target_revision)
|
|
629
|
+
except (AdoptionPlanError, OSError) as error:
|
|
630
|
+
click.echo(json.dumps({"schema": "goal.adoption-plan/v1", "state": "blocked",
|
|
631
|
+
"reason": "invalid_input", "detail": str(error),
|
|
632
|
+
"authority": "advisory-only", "steps": []}, sort_keys=True))
|
|
633
|
+
raise click.exceptions.Exit(2) from error
|
|
634
|
+
click.echo(json.dumps(plan, indent=2, sort_keys=True))
|
|
635
|
+
if plan["state"] == "blocked":
|
|
636
|
+
raise click.exceptions.Exit(2)
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
def _governance_committed(target: Path) -> bool:
|
|
640
|
+
"""True when HEAD already contains an adopted governance manifest."""
|
|
641
|
+
result = subprocess.run(
|
|
642
|
+
["git", "-C", str(target), "cat-file", "-e", "HEAD:.governance/manifest.json"],
|
|
643
|
+
capture_output=True,
|
|
644
|
+
check=False,
|
|
645
|
+
)
|
|
646
|
+
return result.returncode == 0
|
|
647
|
+
|
|
648
|
+
|
|
571
649
|
@governance.command("adopt")
|
|
572
650
|
@click.option(
|
|
573
651
|
"--standard-repository",
|
|
@@ -610,6 +688,15 @@ def verify_delivery(ctx, delivery_mode):
|
|
|
610
688
|
"generator must record non-production provenance."
|
|
611
689
|
),
|
|
612
690
|
)
|
|
691
|
+
@click.option(
|
|
692
|
+
"--bootstrap-native-adoption",
|
|
693
|
+
is_flag=True,
|
|
694
|
+
help=(
|
|
695
|
+
"First adoption into a committed repository without governance: record "
|
|
696
|
+
"the installer receipt that the first --bootstrap-adoption-digest "
|
|
697
|
+
"ticket allocation requires."
|
|
698
|
+
),
|
|
699
|
+
)
|
|
613
700
|
@click.option(
|
|
614
701
|
"--pre-commit",
|
|
615
702
|
is_flag=True,
|
|
@@ -627,6 +714,7 @@ def adopt(
|
|
|
627
714
|
check,
|
|
628
715
|
upgrade,
|
|
629
716
|
allow_unpublished_for_testing,
|
|
717
|
+
bootstrap_native_adoption,
|
|
630
718
|
pre_commit,
|
|
631
719
|
ticket,
|
|
632
720
|
):
|
|
@@ -642,10 +730,12 @@ def adopt(
|
|
|
642
730
|
raise click.UsageError("--check and --upgrade are mutually exclusive")
|
|
643
731
|
if pre_commit and (not latest or not ticket):
|
|
644
732
|
raise click.UsageError("--pre-commit requires --latest and --ticket")
|
|
645
|
-
if pre_commit and (
|
|
733
|
+
if pre_commit and (
|
|
734
|
+
check or upgrade or allow_unpublished_for_testing or bootstrap_native_adoption
|
|
735
|
+
):
|
|
646
736
|
raise click.UsageError(
|
|
647
|
-
"--pre-commit cannot be combined with --check, --upgrade,
|
|
648
|
-
"--allow-unpublished-for-testing"
|
|
737
|
+
"--pre-commit cannot be combined with --check, --upgrade, "
|
|
738
|
+
"--allow-unpublished-for-testing, or --bootstrap-native-adoption"
|
|
649
739
|
)
|
|
650
740
|
|
|
651
741
|
target = target_root.resolve()
|
|
@@ -695,6 +785,9 @@ def adopt(
|
|
|
695
785
|
command.append("--upgrade")
|
|
696
786
|
if allow_unpublished_for_testing:
|
|
697
787
|
command.append("--allow-unpublished-for-testing")
|
|
788
|
+
if bootstrap_native_adoption:
|
|
789
|
+
command.append("--bootstrap-native-adoption")
|
|
790
|
+
fresh_repository = not check and not _governance_committed(target)
|
|
698
791
|
|
|
699
792
|
result = subprocess.run(command, capture_output=True, text=True, check=False)
|
|
700
793
|
if result.stdout:
|
|
@@ -703,6 +796,14 @@ def adopt(
|
|
|
703
796
|
click.echo(result.stderr, err=True, nl=False)
|
|
704
797
|
if result.returncode != 0:
|
|
705
798
|
raise click.exceptions.Exit(result.returncode)
|
|
799
|
+
if fresh_repository and not bootstrap_native_adoption:
|
|
800
|
+
click.echo(
|
|
801
|
+
"note: this repository has no committed governance; its first ticket "
|
|
802
|
+
"allocation needs the bootstrap receipt. Rerun with "
|
|
803
|
+
"--bootstrap-native-adoption, then allocate with "
|
|
804
|
+
"./project/new-ticket.sh --bootstrap-adoption-digest <dirtyDigest>.",
|
|
805
|
+
err=True,
|
|
806
|
+
)
|
|
706
807
|
if pre_commit:
|
|
707
808
|
click.echo(
|
|
708
809
|
f"prepared standard update {current_revision}..{source_revision} "
|