gitgrip 2.0.0a5__tar.gz → 2.0.0a6__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.
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/PKG-INFO +14 -10
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/README.md +12 -9
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gitgrip.egg-info/PKG-INFO +14 -10
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gitgrip.egg-info/SOURCES.txt +38 -1
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gitgrip.egg-info/requires.txt +1 -0
- gitgrip-2.0.0a6/gr2/api_core.py +306 -0
- gitgrip-2.0.0a6/gr2/schemas/gr2-workspace-spec-v1.schema.json +10 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2_overlay/__init__.py +8 -3
- gitgrip-2.0.0a6/prototypes/concurrent_event_stress.py +364 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/lane_workspace_prototype.py +3 -3
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/python_hook_runtime_playground.py +6 -6
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/real_git_playground.py +2 -2
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/repo_maintenance_prototype.py +1 -1
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/pyproject.toml +14 -3
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/app.py +444 -34
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/channel_bridge.py +28 -3
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/clone_exec.py +5 -6
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/commit.py +42 -11
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/consent.py +6 -4
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/events.py +45 -14
- gitgrip-2.0.0a6/python_cli/exit_codes.py +38 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/file_exec.py +5 -5
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/git_review.py +44 -1
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/gitinclude.py +15 -3
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/gitops.py +2 -2
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/grip.py +119 -4
- gitgrip-2.0.0a6/python_cli/grip_cli.py +1975 -0
- gitgrip-2.0.0a6/python_cli/layout.py +102 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/migration.py +123 -12
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/open_gr_review.py +28 -11
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/platform.py +159 -10
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/pr.py +244 -8
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/project_review.py +7 -1
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/push.py +8 -6
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/review.py +58 -21
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/review_dispatch.py +4 -5
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/review_rebind.py +2 -2
- gitgrip-2.0.0a6/python_cli/review_records.py +143 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/review_run.py +343 -32
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/review_runners.py +35 -8
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/spec_apply.py +420 -33
- gitgrip-2.0.0a6/python_cli/version.py +373 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/workspace_snapshot.py +1 -1
- gitgrip-2.0.0a6/tests/test_api_core_seam.py +360 -0
- gitgrip-2.0.0a6/tests/test_api_dump.py +775 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_apply_convergence.py +20 -2
- gitgrip-2.0.0a6/tests/test_apply_gr1_sibling_adoption.py +358 -0
- gitgrip-2.0.0a6/tests/test_break_17_unit_path_containment.py +188 -0
- gitgrip-2.0.0a6/tests/test_break_18_gr1_sibling_units.py +482 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_cli_shape_witnesses.py +1 -1
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_clone_materialization.py +23 -19
- gitgrip-2.0.0a6/tests/test_deprecation_registry.py +277 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_ensure_lane_checkout_reference_clone.py +1 -1
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_event_log_integrity.py +152 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_events.py +14 -10
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_events_torn_line.py +5 -2
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_exec_apostrophe_and_oserror.py +2 -2
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_exec_single_string_and_materialize_plan.py +3 -3
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_git_review.py +201 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_gitinclude_compiler.py +50 -3
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_gr1_first_run_guidance.py +19 -5
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_gr2_review_receipt.py +2 -2
- gitgrip-2.0.0a6/tests/test_grip_cli.py +450 -0
- gitgrip-2.0.0a6/tests/test_grip_toml_schema_v1.py +33 -0
- gitgrip-2.0.0a6/tests/test_hooks_group_names.py +63 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_lane_create_fork_base_cli.py +3 -3
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_lane_repo_paths_and_all_skipped.py +4 -4
- gitgrip-2.0.0a6/tests/test_lane_show_name.py +46 -0
- gitgrip-2.0.0a6/tests/test_layout_census.py +180 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_materialization_plan_contract.py +16 -16
- gitgrip-2.0.0a6/tests/test_member_working_root_checkout_materialize.py +329 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_migration.py +247 -2
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_migration_unadopted_notice.py +10 -8
- gitgrip-2.0.0a6/tests/test_native_store_review_record_probes.py +266 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_open_gr_enter.py +6 -4
- gitgrip-2.0.0a6/tests/test_outbox_ack_after_effect.py +262 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_pr_events.py +1 -0
- gitgrip-2.0.0a6/tests/test_pr_merge_head_pin.py +393 -0
- gitgrip-2.0.0a6/tests/test_pr_merge_missing_target.py +694 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_pr_merge_partial_failure.py +9 -1
- gitgrip-2.0.0a6/tests/test_pr_view.py +502 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_project_file_materialization.py +4 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_project_review_three_repo.py +3 -3
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_propagation_state_machine.py +10 -22
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_readme_verbs_exist.py +67 -2
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_bind_from_range.py +85 -1
- gitgrip-2.0.0a6/tests/test_review_bind_native_store.py +293 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_cli.py +4 -4
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_close_gr.py +49 -4
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_ephemeral.py +1 -1
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_open_lane.py +100 -9
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_rebind.py +22 -1
- gitgrip-2.0.0a6/tests/test_review_receipt_cleanup.py +92 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_run.py +7 -7
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_run_import_isolation.py +8 -1
- gitgrip-2.0.0a6/tests/test_review_run_integrity_recheck.py +361 -0
- gitgrip-2.0.0a6/tests/test_review_run_multi_repo.py +344 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_run_rerun_and_refusal_receipt.py +4 -4
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_runners.py +57 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_verify_inside.py +1 -1
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_rmtree_or_refuse.py +2 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_single_repo_cwd_refusal.py +2 -2
- gitgrip-2.0.0a6/tests/test_snapshot_id_prefix_resolution.py +221 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_sprint21_sync_platform.py +4 -0
- gitgrip-2.0.0a6/tests/test_store_break_attempts.py +1958 -0
- gitgrip-2.0.0a6/tests/test_store_checkout_atomicity.py +405 -0
- gitgrip-2.0.0a6/tests/test_store_commit_noop.py +137 -0
- gitgrip-2.0.0a6/tests/test_store_declared_members.py +293 -0
- gitgrip-2.0.0a6/tests/test_store_git_native_smoke.py +220 -0
- gitgrip-2.0.0a6/tests/test_store_init_branch_inference.py +260 -0
- gitgrip-2.0.0a6/tests/test_store_log_adoption_boundary.py +205 -0
- gitgrip-2.0.0a6/tests/test_store_member_path_hygiene.py +128 -0
- gitgrip-2.0.0a6/tests/test_store_member_state.py +213 -0
- gitgrip-2.0.0a6/tests/test_store_migrate_branch_inference.py +106 -0
- gitgrip-2.0.0a6/tests/test_store_status_values.py +184 -0
- gitgrip-2.0.0a6/tests/test_store_verb_rename.py +140 -0
- gitgrip-2.0.0a6/tests/test_store_verb_witnesses.py +770 -0
- gitgrip-2.0.0a6/tests/test_version_identity.py +937 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_version_static.py +31 -8
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_workspace_convert_clone.py +1 -1
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_workspace_identity_boundary.py +11 -1
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_workspace_init_local_repos.py +3 -3
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_workspace_snapshot.py +1 -1
- gitgrip-2.0.0a5/prototypes/concurrent_event_stress.py +0 -199
- gitgrip-2.0.0a5/python_cli/grip_cli.py +0 -565
- gitgrip-2.0.0a5/tests/test_grip_cli.py +0 -536
- gitgrip-2.0.0a5/tests/test_grip_object_model.py +0 -357
- gitgrip-2.0.0a5/tests/test_snapshot_id_prefix_resolution.py +0 -132
- gitgrip-2.0.0a5/tests/test_store_member_state.py +0 -138
- gitgrip-2.0.0a5/tests/test_store_verb_rename.py +0 -46
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gitgrip.egg-info/dependency_links.txt +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gitgrip.egg-info/entry_points.txt +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gitgrip.egg-info/top_level.txt +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/__init__.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/__init__.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/activate.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/agent_manifest.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/cli.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/cross_repo.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/drivers.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/introspection.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/language_drivers.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/objects.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/perf.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/refs.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/trust.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/types.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/units.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/overlay/workspace_spec.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/gr2/schemas/gr2-materialization-plan-v1.schema.json +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/__init__.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/cache_materialization_probe.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/concurrent_lease_stress.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/concurrent_workspace_cap_stress.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/contribution_protocol.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/cross_mode_lane_stress.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/jsonl_store.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/layout_model_probe.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/propagation_daemon.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/propagation_state_machine.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/python_exec_playground.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/python_migration_playground.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/python_review_checkout_playground.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/python_spec_apply_playground.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/real_git_lane_materialization.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/recall_lane_history.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/prototypes/repo_transport_probe.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/__init__.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/__main__.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/add.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/branch.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/config.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/env_exec.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/execops.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/failures.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/hooks.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/launch_exec.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/merge_verification.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/prune.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/review_ephemeral.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/staging_cleanup.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/syncops.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/target.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/python_cli/workspace_guidance.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/setup.cfg +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_add.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_agent_manifest_overlay_source.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_app_event_outcomes.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_branch.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_channel_bridge.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_cli_adapter_error_boundary.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_commit.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_config_base_path_guard.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_config_overlay.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_conftest_git_isolation.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_contribution_protocol.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_env_materialization.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_event_outcome_policy.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_execops.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_failure_event_outcome.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_first_five_minutes.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_foreground_launch_runtime.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_gr2_packaging.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_grip_hardening.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_grip_snapshot.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_hook_events.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_hook_hardening.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_hooks_consent.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_jsonl_store.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_lane_create_refusal_leaves_nothing.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_lane_create_refuses_own_branch.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_lane_enter_hook_warnings.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_lane_lease_show_json.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_lane_lifecycle_safety.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_lane_toml_serialization.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_launch_primitive.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_m2_overlay_hindsight.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_m2_overlay_mem0.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_m2_overlay_zep.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_materialize_pin.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_merge_evidence.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_merge_parent_verification.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_migration_overlay_compatibility.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_no_bare_python_cli_imports.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_null_current_lane_refusal.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_open_project_refusal_reason_cli.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_acceptance.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_activate.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_cli.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_cli_wiring.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_composition_merge.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_cross_repo_atomic_apply.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_cross_repo_perf_gate.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_cross_repo_rollback.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_driver_registry.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_introspection.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_multi_stack.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_object_encoding.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_perf_gates.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_python_driver.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_tier_b_encoding.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_tier_b_perf_gates.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_tier_b_repo_state.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_tier_b_staging_discipline.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_trust_model.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_overlay_workspace_spec.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_pr_group_legibility.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_pr_merge_method.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_project_review_range_reconstruction.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_projection_tracked_dest.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_propagation_contribution.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_propagation_daemon.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_prune.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_push.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_repo_path_read_through.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_repo_status_linked_worktree.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_repo_status_schema_version.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_repo_status_symlinked_git_dir.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_bind_fork_base.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_bind_verify.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_create_project_cli.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_review_dispatch.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_spec_apply_event_outcomes.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_spec_declaration_ref_pin.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_staging_cleanup.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_submodule_member_predicate.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_superproject_import.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_sync_event_outcomes.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_sync_fast_forward.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_sync_fetch.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_sync_seed_local.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_sync_ssh_rewrite.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_sync_status_probe.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_target.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_tmux_launch_runtime.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_unit_abort.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_unit_apply.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_unit_apply_dry_run.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_unit_manifest_schema.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_unit_propose.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_unit_topological_apply.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_validate_repo_path_conflict.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_workspace_edit_lease_cap.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_workspace_init_from_topology.py +0 -0
- {gitgrip-2.0.0a5 → gitgrip-2.0.0a6}/tests/test_workspace_root_defaults.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitgrip
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.0a6
|
|
4
4
|
Summary: Workspace layer for multi-repo work: workspaces, isolated lanes, and one grouped review per slice
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -12,6 +12,7 @@ Requires-Dist: jsonschema>=4.18
|
|
|
12
12
|
Provides-Extra: dev
|
|
13
13
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
14
14
|
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
15
|
+
Requires-Dist: packaging>=22; extra == "dev"
|
|
15
16
|
|
|
16
17
|
# gr2
|
|
17
18
|
|
|
@@ -76,7 +77,7 @@ gr2 commit -m "my change" --workspace-root ~/ws --owner-unit default
|
|
|
76
77
|
```bash
|
|
77
78
|
gr2 exec run ~/ws default --actor human:you -- pytest -q # one command, every lane repo
|
|
78
79
|
gr2 review create-project ~/ws default feat-x # prints gr:<sha>: one object to review
|
|
79
|
-
gr2 lane
|
|
80
|
+
gr2 lane show ~/ws default # where am I?
|
|
80
81
|
gr2 lane exit ~/ws default --actor human:you
|
|
81
82
|
```
|
|
82
83
|
|
|
@@ -86,7 +87,7 @@ gr2 lane exit ~/ws default --actor human:you
|
|
|
86
87
|
|
|
87
88
|
Alpha 2 is a working local workspace layer. Verified by running the walk above:
|
|
88
89
|
|
|
89
|
-
- `workspace` (init, materialize, status, migrate-gr1 from a gr1 gripspace), `lane` (create, enter, exit, current, lease), `store` (init,
|
|
90
|
+
- `workspace` (init, materialize, status, migrate-gr1 from a gr1 gripspace), `lane` (create, enter, exit, current, lease), `store` (init, commit, log, diff, checkout), `exec run`, `repo status`, `sync status`, `review create-project`.
|
|
90
91
|
- Lanes are independent clones: two lanes, or two agents, never share refs, an index, or a working tree.
|
|
91
92
|
- A lane commit that commits nothing anywhere refuses loudly instead of reporting success.
|
|
92
93
|
|
|
@@ -100,17 +101,18 @@ Not there yet:
|
|
|
100
101
|
|
|
101
102
|
| Group | Verbs |
|
|
102
103
|
|---|---|
|
|
103
|
-
| `workspace` | init, init-from-topology, materialize, status, convert-clone, detect-gr1, migrate-gr1, migrate-lane-state, bootstrap-gr1 |
|
|
104
|
+
| `workspace` | init, init-from-topology, materialize, status, convert-clone, detect-gr1, migrate-gr1, migrate-lane-state, bootstrap-gr1, gitinclude |
|
|
104
105
|
| `spec` | show, validate |
|
|
105
|
-
| `lane` | create, enter, exit,
|
|
106
|
-
| (top level) | branch, add, commit, push, prune |
|
|
106
|
+
| `lane` | create, enter, exit, show, resolve, bind, lease |
|
|
107
|
+
| (top level) | branch, add, commit, push, prune, status, plan, apply |
|
|
107
108
|
| `sync` | status, run |
|
|
108
|
-
| `pr` | create, status, checks, merge |
|
|
109
|
+
| `pr` | create, status, checks, merge, view |
|
|
109
110
|
| `review` | open, close, checkout-pr, run, requirements, bind, verify, rebind, create-project |
|
|
110
111
|
| `exec` | status, run |
|
|
111
|
-
| `repo` | status,
|
|
112
|
-
| `
|
|
113
|
-
| `
|
|
112
|
+
| `repo` | status, projection-run |
|
|
113
|
+
| `hooks` | trust, revoke, status, show, run |
|
|
114
|
+
| `store` | init, commit, check, push, status, log, diff, checkout, materialize, migrate |
|
|
115
|
+
| `target`, `config` | stored PR target; config overlays |
|
|
114
116
|
|
|
115
117
|
## Overlay substrate
|
|
116
118
|
|
|
@@ -123,3 +125,5 @@ pip install -e "gr2[dev]" # from the repository root
|
|
|
123
125
|
cd gr2 && pytest # the gr2 suite (tests + overlay tests)
|
|
124
126
|
cd gr2 && ruff check . # lint, same config as CI
|
|
125
127
|
```
|
|
128
|
+
|
|
129
|
+
The Rust development binary is **`gr2-dev`** — built by `cargo build -p gr2-cli --release`, `publish = false`, and not shipped with `cargo install gitgrip`. It answered to the name `gr2` until 2026-09-30, so a branch written before that rename goes red on `cargo test -p gr2-cli` with `CARGO_BIN_EXE_gr2 is unset`; the repair is `cargo_bin("gr2-dev")`, from which Cargo derives `CARGO_BIN_EXE_gr2-dev`.
|
|
@@ -61,7 +61,7 @@ gr2 commit -m "my change" --workspace-root ~/ws --owner-unit default
|
|
|
61
61
|
```bash
|
|
62
62
|
gr2 exec run ~/ws default --actor human:you -- pytest -q # one command, every lane repo
|
|
63
63
|
gr2 review create-project ~/ws default feat-x # prints gr:<sha>: one object to review
|
|
64
|
-
gr2 lane
|
|
64
|
+
gr2 lane show ~/ws default # where am I?
|
|
65
65
|
gr2 lane exit ~/ws default --actor human:you
|
|
66
66
|
```
|
|
67
67
|
|
|
@@ -71,7 +71,7 @@ gr2 lane exit ~/ws default --actor human:you
|
|
|
71
71
|
|
|
72
72
|
Alpha 2 is a working local workspace layer. Verified by running the walk above:
|
|
73
73
|
|
|
74
|
-
- `workspace` (init, materialize, status, migrate-gr1 from a gr1 gripspace), `lane` (create, enter, exit, current, lease), `store` (init,
|
|
74
|
+
- `workspace` (init, materialize, status, migrate-gr1 from a gr1 gripspace), `lane` (create, enter, exit, current, lease), `store` (init, commit, log, diff, checkout), `exec run`, `repo status`, `sync status`, `review create-project`.
|
|
75
75
|
- Lanes are independent clones: two lanes, or two agents, never share refs, an index, or a working tree.
|
|
76
76
|
- A lane commit that commits nothing anywhere refuses loudly instead of reporting success.
|
|
77
77
|
|
|
@@ -85,17 +85,18 @@ Not there yet:
|
|
|
85
85
|
|
|
86
86
|
| Group | Verbs |
|
|
87
87
|
|---|---|
|
|
88
|
-
| `workspace` | init, init-from-topology, materialize, status, convert-clone, detect-gr1, migrate-gr1, migrate-lane-state, bootstrap-gr1 |
|
|
88
|
+
| `workspace` | init, init-from-topology, materialize, status, convert-clone, detect-gr1, migrate-gr1, migrate-lane-state, bootstrap-gr1, gitinclude |
|
|
89
89
|
| `spec` | show, validate |
|
|
90
|
-
| `lane` | create, enter, exit,
|
|
91
|
-
| (top level) | branch, add, commit, push, prune |
|
|
90
|
+
| `lane` | create, enter, exit, show, resolve, bind, lease |
|
|
91
|
+
| (top level) | branch, add, commit, push, prune, status, plan, apply |
|
|
92
92
|
| `sync` | status, run |
|
|
93
|
-
| `pr` | create, status, checks, merge |
|
|
93
|
+
| `pr` | create, status, checks, merge, view |
|
|
94
94
|
| `review` | open, close, checkout-pr, run, requirements, bind, verify, rebind, create-project |
|
|
95
95
|
| `exec` | status, run |
|
|
96
|
-
| `repo` | status,
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
96
|
+
| `repo` | status, projection-run |
|
|
97
|
+
| `hooks` | trust, revoke, status, show, run |
|
|
98
|
+
| `store` | init, commit, check, push, status, log, diff, checkout, materialize, migrate |
|
|
99
|
+
| `target`, `config` | stored PR target; config overlays |
|
|
99
100
|
|
|
100
101
|
## Overlay substrate
|
|
101
102
|
|
|
@@ -108,3 +109,5 @@ pip install -e "gr2[dev]" # from the repository root
|
|
|
108
109
|
cd gr2 && pytest # the gr2 suite (tests + overlay tests)
|
|
109
110
|
cd gr2 && ruff check . # lint, same config as CI
|
|
110
111
|
```
|
|
112
|
+
|
|
113
|
+
The Rust development binary is **`gr2-dev`** — built by `cargo build -p gr2-cli --release`, `publish = false`, and not shipped with `cargo install gitgrip`. It answered to the name `gr2` until 2026-09-30, so a branch written before that rename goes red on `cargo test -p gr2-cli` with `CARGO_BIN_EXE_gr2 is unset`; the repair is `cargo_bin("gr2-dev")`, from which Cargo derives `CARGO_BIN_EXE_gr2-dev`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitgrip
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.0a6
|
|
4
4
|
Summary: Workspace layer for multi-repo work: workspaces, isolated lanes, and one grouped review per slice
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -12,6 +12,7 @@ Requires-Dist: jsonschema>=4.18
|
|
|
12
12
|
Provides-Extra: dev
|
|
13
13
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
14
14
|
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
15
|
+
Requires-Dist: packaging>=22; extra == "dev"
|
|
15
16
|
|
|
16
17
|
# gr2
|
|
17
18
|
|
|
@@ -76,7 +77,7 @@ gr2 commit -m "my change" --workspace-root ~/ws --owner-unit default
|
|
|
76
77
|
```bash
|
|
77
78
|
gr2 exec run ~/ws default --actor human:you -- pytest -q # one command, every lane repo
|
|
78
79
|
gr2 review create-project ~/ws default feat-x # prints gr:<sha>: one object to review
|
|
79
|
-
gr2 lane
|
|
80
|
+
gr2 lane show ~/ws default # where am I?
|
|
80
81
|
gr2 lane exit ~/ws default --actor human:you
|
|
81
82
|
```
|
|
82
83
|
|
|
@@ -86,7 +87,7 @@ gr2 lane exit ~/ws default --actor human:you
|
|
|
86
87
|
|
|
87
88
|
Alpha 2 is a working local workspace layer. Verified by running the walk above:
|
|
88
89
|
|
|
89
|
-
- `workspace` (init, materialize, status, migrate-gr1 from a gr1 gripspace), `lane` (create, enter, exit, current, lease), `store` (init,
|
|
90
|
+
- `workspace` (init, materialize, status, migrate-gr1 from a gr1 gripspace), `lane` (create, enter, exit, current, lease), `store` (init, commit, log, diff, checkout), `exec run`, `repo status`, `sync status`, `review create-project`.
|
|
90
91
|
- Lanes are independent clones: two lanes, or two agents, never share refs, an index, or a working tree.
|
|
91
92
|
- A lane commit that commits nothing anywhere refuses loudly instead of reporting success.
|
|
92
93
|
|
|
@@ -100,17 +101,18 @@ Not there yet:
|
|
|
100
101
|
|
|
101
102
|
| Group | Verbs |
|
|
102
103
|
|---|---|
|
|
103
|
-
| `workspace` | init, init-from-topology, materialize, status, convert-clone, detect-gr1, migrate-gr1, migrate-lane-state, bootstrap-gr1 |
|
|
104
|
+
| `workspace` | init, init-from-topology, materialize, status, convert-clone, detect-gr1, migrate-gr1, migrate-lane-state, bootstrap-gr1, gitinclude |
|
|
104
105
|
| `spec` | show, validate |
|
|
105
|
-
| `lane` | create, enter, exit,
|
|
106
|
-
| (top level) | branch, add, commit, push, prune |
|
|
106
|
+
| `lane` | create, enter, exit, show, resolve, bind, lease |
|
|
107
|
+
| (top level) | branch, add, commit, push, prune, status, plan, apply |
|
|
107
108
|
| `sync` | status, run |
|
|
108
|
-
| `pr` | create, status, checks, merge |
|
|
109
|
+
| `pr` | create, status, checks, merge, view |
|
|
109
110
|
| `review` | open, close, checkout-pr, run, requirements, bind, verify, rebind, create-project |
|
|
110
111
|
| `exec` | status, run |
|
|
111
|
-
| `repo` | status,
|
|
112
|
-
| `
|
|
113
|
-
| `
|
|
112
|
+
| `repo` | status, projection-run |
|
|
113
|
+
| `hooks` | trust, revoke, status, show, run |
|
|
114
|
+
| `store` | init, commit, check, push, status, log, diff, checkout, materialize, migrate |
|
|
115
|
+
| `target`, `config` | stored PR target; config overlays |
|
|
114
116
|
|
|
115
117
|
## Overlay substrate
|
|
116
118
|
|
|
@@ -123,3 +125,5 @@ pip install -e "gr2[dev]" # from the repository root
|
|
|
123
125
|
cd gr2 && pytest # the gr2 suite (tests + overlay tests)
|
|
124
126
|
cd gr2 && ruff check . # lint, same config as CI
|
|
125
127
|
```
|
|
128
|
+
|
|
129
|
+
The Rust development binary is **`gr2-dev`** — built by `cargo build -p gr2-cli --release`, `publish = false`, and not shipped with `cargo install gitgrip`. It answered to the name `gr2` until 2026-09-30, so a branch written before that rename goes red on `cargo test -p gr2-cli` with `CARGO_BIN_EXE_gr2 is unset`; the repair is `cargo_bin("gr2-dev")`, from which Cargo derives `CARGO_BIN_EXE_gr2-dev`.
|
|
@@ -7,6 +7,7 @@ gitgrip.egg-info/entry_points.txt
|
|
|
7
7
|
gitgrip.egg-info/requires.txt
|
|
8
8
|
gitgrip.egg-info/top_level.txt
|
|
9
9
|
gr2/__init__.py
|
|
10
|
+
gr2/api_core.py
|
|
10
11
|
gr2/overlay/__init__.py
|
|
11
12
|
gr2/overlay/activate.py
|
|
12
13
|
gr2/overlay/agent_manifest.py
|
|
@@ -23,6 +24,7 @@ gr2/overlay/types.py
|
|
|
23
24
|
gr2/overlay/units.py
|
|
24
25
|
gr2/overlay/workspace_spec.py
|
|
25
26
|
gr2/schemas/gr2-materialization-plan-v1.schema.json
|
|
27
|
+
gr2/schemas/gr2-workspace-spec-v1.schema.json
|
|
26
28
|
gr2_overlay/__init__.py
|
|
27
29
|
prototypes/__init__.py
|
|
28
30
|
prototypes/cache_materialization_probe.py
|
|
@@ -59,6 +61,7 @@ python_cli/consent.py
|
|
|
59
61
|
python_cli/env_exec.py
|
|
60
62
|
python_cli/events.py
|
|
61
63
|
python_cli/execops.py
|
|
64
|
+
python_cli/exit_codes.py
|
|
62
65
|
python_cli/failures.py
|
|
63
66
|
python_cli/file_exec.py
|
|
64
67
|
python_cli/git_review.py
|
|
@@ -68,6 +71,7 @@ python_cli/grip.py
|
|
|
68
71
|
python_cli/grip_cli.py
|
|
69
72
|
python_cli/hooks.py
|
|
70
73
|
python_cli/launch_exec.py
|
|
74
|
+
python_cli/layout.py
|
|
71
75
|
python_cli/merge_verification.py
|
|
72
76
|
python_cli/migration.py
|
|
73
77
|
python_cli/open_gr_review.py
|
|
@@ -80,19 +84,26 @@ python_cli/review.py
|
|
|
80
84
|
python_cli/review_dispatch.py
|
|
81
85
|
python_cli/review_ephemeral.py
|
|
82
86
|
python_cli/review_rebind.py
|
|
87
|
+
python_cli/review_records.py
|
|
83
88
|
python_cli/review_run.py
|
|
84
89
|
python_cli/review_runners.py
|
|
85
90
|
python_cli/spec_apply.py
|
|
86
91
|
python_cli/staging_cleanup.py
|
|
87
92
|
python_cli/syncops.py
|
|
88
93
|
python_cli/target.py
|
|
94
|
+
python_cli/version.py
|
|
89
95
|
python_cli/workspace_guidance.py
|
|
90
96
|
python_cli/workspace_snapshot.py
|
|
91
97
|
tests/test_add.py
|
|
92
98
|
tests/test_agent_manifest_overlay_source.py
|
|
99
|
+
tests/test_api_core_seam.py
|
|
100
|
+
tests/test_api_dump.py
|
|
93
101
|
tests/test_app_event_outcomes.py
|
|
94
102
|
tests/test_apply_convergence.py
|
|
103
|
+
tests/test_apply_gr1_sibling_adoption.py
|
|
95
104
|
tests/test_branch.py
|
|
105
|
+
tests/test_break_17_unit_path_containment.py
|
|
106
|
+
tests/test_break_18_gr1_sibling_units.py
|
|
96
107
|
tests/test_channel_bridge.py
|
|
97
108
|
tests/test_cli_adapter_error_boundary.py
|
|
98
109
|
tests/test_cli_shape_witnesses.py
|
|
@@ -102,6 +113,7 @@ tests/test_config_base_path_guard.py
|
|
|
102
113
|
tests/test_config_overlay.py
|
|
103
114
|
tests/test_conftest_git_isolation.py
|
|
104
115
|
tests/test_contribution_protocol.py
|
|
116
|
+
tests/test_deprecation_registry.py
|
|
105
117
|
tests/test_ensure_lane_checkout_reference_clone.py
|
|
106
118
|
tests/test_env_materialization.py
|
|
107
119
|
tests/test_event_log_integrity.py
|
|
@@ -121,11 +133,12 @@ tests/test_gr2_packaging.py
|
|
|
121
133
|
tests/test_gr2_review_receipt.py
|
|
122
134
|
tests/test_grip_cli.py
|
|
123
135
|
tests/test_grip_hardening.py
|
|
124
|
-
tests/test_grip_object_model.py
|
|
125
136
|
tests/test_grip_snapshot.py
|
|
137
|
+
tests/test_grip_toml_schema_v1.py
|
|
126
138
|
tests/test_hook_events.py
|
|
127
139
|
tests/test_hook_hardening.py
|
|
128
140
|
tests/test_hooks_consent.py
|
|
141
|
+
tests/test_hooks_group_names.py
|
|
129
142
|
tests/test_jsonl_store.py
|
|
130
143
|
tests/test_lane_create_fork_base_cli.py
|
|
131
144
|
tests/test_lane_create_refusal_leaves_nothing.py
|
|
@@ -134,22 +147,27 @@ tests/test_lane_enter_hook_warnings.py
|
|
|
134
147
|
tests/test_lane_lease_show_json.py
|
|
135
148
|
tests/test_lane_lifecycle_safety.py
|
|
136
149
|
tests/test_lane_repo_paths_and_all_skipped.py
|
|
150
|
+
tests/test_lane_show_name.py
|
|
137
151
|
tests/test_lane_toml_serialization.py
|
|
138
152
|
tests/test_launch_primitive.py
|
|
153
|
+
tests/test_layout_census.py
|
|
139
154
|
tests/test_m2_overlay_hindsight.py
|
|
140
155
|
tests/test_m2_overlay_mem0.py
|
|
141
156
|
tests/test_m2_overlay_zep.py
|
|
142
157
|
tests/test_materialization_plan_contract.py
|
|
143
158
|
tests/test_materialize_pin.py
|
|
159
|
+
tests/test_member_working_root_checkout_materialize.py
|
|
144
160
|
tests/test_merge_evidence.py
|
|
145
161
|
tests/test_merge_parent_verification.py
|
|
146
162
|
tests/test_migration.py
|
|
147
163
|
tests/test_migration_overlay_compatibility.py
|
|
148
164
|
tests/test_migration_unadopted_notice.py
|
|
165
|
+
tests/test_native_store_review_record_probes.py
|
|
149
166
|
tests/test_no_bare_python_cli_imports.py
|
|
150
167
|
tests/test_null_current_lane_refusal.py
|
|
151
168
|
tests/test_open_gr_enter.py
|
|
152
169
|
tests/test_open_project_refusal_reason_cli.py
|
|
170
|
+
tests/test_outbox_ack_after_effect.py
|
|
153
171
|
tests/test_overlay_acceptance.py
|
|
154
172
|
tests/test_overlay_activate.py
|
|
155
173
|
tests/test_overlay_cli.py
|
|
@@ -172,8 +190,11 @@ tests/test_overlay_trust_model.py
|
|
|
172
190
|
tests/test_overlay_workspace_spec.py
|
|
173
191
|
tests/test_pr_events.py
|
|
174
192
|
tests/test_pr_group_legibility.py
|
|
193
|
+
tests/test_pr_merge_head_pin.py
|
|
175
194
|
tests/test_pr_merge_method.py
|
|
195
|
+
tests/test_pr_merge_missing_target.py
|
|
176
196
|
tests/test_pr_merge_partial_failure.py
|
|
197
|
+
tests/test_pr_view.py
|
|
177
198
|
tests/test_project_file_materialization.py
|
|
178
199
|
tests/test_project_review_range_reconstruction.py
|
|
179
200
|
tests/test_project_review_three_repo.py
|
|
@@ -190,6 +211,7 @@ tests/test_repo_status_schema_version.py
|
|
|
190
211
|
tests/test_repo_status_symlinked_git_dir.py
|
|
191
212
|
tests/test_review_bind_fork_base.py
|
|
192
213
|
tests/test_review_bind_from_range.py
|
|
214
|
+
tests/test_review_bind_native_store.py
|
|
193
215
|
tests/test_review_bind_verify.py
|
|
194
216
|
tests/test_review_cli.py
|
|
195
217
|
tests/test_review_close_gr.py
|
|
@@ -198,8 +220,11 @@ tests/test_review_dispatch.py
|
|
|
198
220
|
tests/test_review_ephemeral.py
|
|
199
221
|
tests/test_review_open_lane.py
|
|
200
222
|
tests/test_review_rebind.py
|
|
223
|
+
tests/test_review_receipt_cleanup.py
|
|
201
224
|
tests/test_review_run.py
|
|
202
225
|
tests/test_review_run_import_isolation.py
|
|
226
|
+
tests/test_review_run_integrity_recheck.py
|
|
227
|
+
tests/test_review_run_multi_repo.py
|
|
203
228
|
tests/test_review_run_rerun_and_refusal_receipt.py
|
|
204
229
|
tests/test_review_runners.py
|
|
205
230
|
tests/test_review_verify_inside.py
|
|
@@ -210,8 +235,19 @@ tests/test_spec_apply_event_outcomes.py
|
|
|
210
235
|
tests/test_spec_declaration_ref_pin.py
|
|
211
236
|
tests/test_sprint21_sync_platform.py
|
|
212
237
|
tests/test_staging_cleanup.py
|
|
238
|
+
tests/test_store_break_attempts.py
|
|
239
|
+
tests/test_store_checkout_atomicity.py
|
|
240
|
+
tests/test_store_commit_noop.py
|
|
241
|
+
tests/test_store_declared_members.py
|
|
242
|
+
tests/test_store_git_native_smoke.py
|
|
243
|
+
tests/test_store_init_branch_inference.py
|
|
244
|
+
tests/test_store_log_adoption_boundary.py
|
|
245
|
+
tests/test_store_member_path_hygiene.py
|
|
213
246
|
tests/test_store_member_state.py
|
|
247
|
+
tests/test_store_migrate_branch_inference.py
|
|
248
|
+
tests/test_store_status_values.py
|
|
214
249
|
tests/test_store_verb_rename.py
|
|
250
|
+
tests/test_store_verb_witnesses.py
|
|
215
251
|
tests/test_submodule_member_predicate.py
|
|
216
252
|
tests/test_superproject_import.py
|
|
217
253
|
tests/test_sync_event_outcomes.py
|
|
@@ -229,6 +265,7 @@ tests/test_unit_manifest_schema.py
|
|
|
229
265
|
tests/test_unit_propose.py
|
|
230
266
|
tests/test_unit_topological_apply.py
|
|
231
267
|
tests/test_validate_repo_path_conflict.py
|
|
268
|
+
tests/test_version_identity.py
|
|
232
269
|
tests/test_version_static.py
|
|
233
270
|
tests/test_workspace_convert_clone.py
|
|
234
271
|
tests/test_workspace_edit_lease_cap.py
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""The generic core of the API dumper: walker, line format, marker lookup.
|
|
2
|
+
|
|
3
|
+
THE SEAM. This module knows four things -- how to walk a command tree, how an
|
|
4
|
+
``.api`` line is shaped, how a stability table is keyed, and how items are
|
|
5
|
+
GROUPED into blocks -- and it knows nothing about gr2: not its app, not its
|
|
6
|
+
tables, not its exit codes. Everything that names gr2 lives in the consumer
|
|
7
|
+
(``gr2/scripts/dump_api.py``), which REGISTERS the kinds it emits, supplies the
|
|
8
|
+
marker rules for them, and says which COMMAND each item belongs to (``Row.group``).
|
|
9
|
+
Grouping is a core rule over a consumer-supplied fact, which is the same
|
|
10
|
+
division as the marker table: the core owns the shape, the consumer owns the
|
|
11
|
+
meaning.
|
|
12
|
+
|
|
13
|
+
The boundary is a test, not a convention: ``tests/test_api_core_seam.py`` walks
|
|
14
|
+
this module's imports at any depth and fails if any of them reaches gr2, with
|
|
15
|
+
the plugin beside it as the control that proves the walker can fail.
|
|
16
|
+
|
|
17
|
+
WHY IT IS WORTH THE SPLIT. The dumper does not move to its own repo yet -- but
|
|
18
|
+
the regroup of the dump by command and subcommand is the moment to cut the
|
|
19
|
+
seam, so that the later extraction is a MOVE rather than a rewrite. A core with
|
|
20
|
+
no gr2 import can be lifted out with its tests; one that reaches into
|
|
21
|
+
``grip_cli`` for an exit prefix cannot.
|
|
22
|
+
|
|
23
|
+
WHAT IS NOT HERE YET, named rather than implied: the alias registry
|
|
24
|
+
(``grip_cli.STORE_MOUNTS`` is gr2's own mount table) and the three gates are
|
|
25
|
+
still on the plugin side, because both answer questions about gr2's surface
|
|
26
|
+
rather than about the format. They move when a second consumer exists to make
|
|
27
|
+
their generic shape decidable.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import tomllib
|
|
33
|
+
from collections.abc import Callable
|
|
34
|
+
from dataclasses import dataclass
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
from typing import NamedTuple
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Row(NamedTuple):
|
|
40
|
+
"""One item, before it is rendered.
|
|
41
|
+
|
|
42
|
+
``group`` is the COMMAND PATH the item belongs to, and the CONSUMER supplies
|
|
43
|
+
it rather than the core deriving one. Only the consumer knows what a command
|
|
44
|
+
is in its own tree: the walk answers for a verb and the flags beneath it, but
|
|
45
|
+
an exit code belongs to a GROUP and a layout path belongs to no verb at all.
|
|
46
|
+
Deriving it here would mean the core guessing at the shape of a spelling.
|
|
47
|
+
|
|
48
|
+
An EMPTY group means "attached to no command" and renders under the
|
|
49
|
+
``UNGROUPED`` heading. A heading is what separates one block from the next,
|
|
50
|
+
so the ungrouped block is given one rather than left as rows appended to
|
|
51
|
+
whichever command sorted before them.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
kind: str
|
|
55
|
+
spelling: str
|
|
56
|
+
hidden: bool = False
|
|
57
|
+
hidden_by: str | None = None
|
|
58
|
+
group: str = ""
|
|
59
|
+
|
|
60
|
+
# (kind, spelling) -> "may-change" | "reserved". An item absent here is stable.
|
|
61
|
+
Markers = dict[tuple[str, str], str]
|
|
62
|
+
|
|
63
|
+
# The spelling column is padded to this width so the stability markers line up
|
|
64
|
+
# and a diff reads as a change to one item rather than to the whole block.
|
|
65
|
+
COLUMN = 64
|
|
66
|
+
|
|
67
|
+
# The heading an item renders under when its consumer gave it no command.
|
|
68
|
+
#
|
|
69
|
+
# NAMED RATHER THAN LEFT BLANK, because a heading is the only thing separating
|
|
70
|
+
# one block from the next: an ungrouped row printed with NO heading lands
|
|
71
|
+
# directly under the last command's items and reads as one of them. The blank
|
|
72
|
+
# version looks tidier in the one case it helps -- a consumer that groups nothing
|
|
73
|
+
# -- and misleads in the case that matters, which is a consumer that groups
|
|
74
|
+
# almost everything and forgets one.
|
|
75
|
+
UNGROUPED = "(ungrouped)"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def walk(cmd, prefix: tuple[str, ...] = (), hidden_by: str | None = None):
|
|
79
|
+
"""Every leaf command, as (path, command, hidden_by).
|
|
80
|
+
|
|
81
|
+
Hidden is INHERITED: a command is hidden when its own flag says so OR any
|
|
82
|
+
ancestor group is hidden. Typer marks a sub-app hidden at the GROUP level
|
|
83
|
+
(``add_typer(app, name="grip", hidden=True)``), and the group's children
|
|
84
|
+
keep ``hidden=False`` of their own -- so reading only the leaf reports a
|
|
85
|
+
hidden group's whole subtree as part of the visible surface. Measured: the
|
|
86
|
+
``grip`` alias group is hidden and 28 of its items were dumped as visible.
|
|
87
|
+
|
|
88
|
+
``hidden_by`` NAMES the ancestor group that did the hiding, because the dump
|
|
89
|
+
carries no group row of its own: without the name, one group flag reads as
|
|
90
|
+
dozens of identical lines and the cause is invisible to a reader.
|
|
91
|
+
"""
|
|
92
|
+
subs = getattr(cmd, "commands", None)
|
|
93
|
+
if subs:
|
|
94
|
+
if getattr(cmd, "hidden", False) and prefix:
|
|
95
|
+
hidden_by = prefix[-1]
|
|
96
|
+
out: list[tuple[tuple[str, ...], object, str | None]] = []
|
|
97
|
+
for name, sub in subs.items():
|
|
98
|
+
out.extend(walk(sub, prefix + (name,), hidden_by))
|
|
99
|
+
return out
|
|
100
|
+
return [(prefix, cmd, hidden_by)]
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def label(spelling: str, hidden: bool, hidden_by: str | None) -> str:
|
|
104
|
+
"""The label column for one item -- the text BEFORE any parsing strips it.
|
|
105
|
+
|
|
106
|
+
One home for the rule, because a gate that wants to ask a question about the
|
|
107
|
+
label TEXT (does it end in a space? how long is it?) cannot use the parsed
|
|
108
|
+
rows: a parser strips the padding, and the trailing space it would be asking
|
|
109
|
+
about is exactly what stripping removes. Measured -- a first version of
|
|
110
|
+
``test_no_label_ends_in_a_space`` read labels through a parser and could not
|
|
111
|
+
have failed for any input, because every label it saw had already been
|
|
112
|
+
stripped.
|
|
113
|
+
|
|
114
|
+
An inherited hide names the group that did it; a command hidden in its own
|
|
115
|
+
right is just ``(hidden)``. The dump carries no group row, so the name is the
|
|
116
|
+
only place the cause can live.
|
|
117
|
+
"""
|
|
118
|
+
if not hidden:
|
|
119
|
+
return spelling
|
|
120
|
+
return f"{spelling} (hidden:{hidden_by})" if hidden_by else f"{spelling} (hidden)"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def line(kind: str, label: str, marker: str) -> str:
|
|
124
|
+
"""One ``.api`` line: two fixed columns, with the separator GUARANTEED.
|
|
125
|
+
|
|
126
|
+
A label at or past ``COLUMN`` is emitted at its own length, so padding it to
|
|
127
|
+
``COLUMN`` adds nothing and the marker runs straight into the label. Every
|
|
128
|
+
label in this dump contains spaces (a flag reads ``verb --flag <str>``), so a
|
|
129
|
+
reader splitting the two columns then takes a space INSIDE the label as the
|
|
130
|
+
separator and recovers neither column.
|
|
131
|
+
|
|
132
|
+
Measured before this rule existed: the 65-char ``exit`` label rendered as
|
|
133
|
+
``...store verb: "may-change``, whose last space is the one inside the quoted
|
|
134
|
+
prefix. The columns came back as the label truncated at ``verb:`` and a
|
|
135
|
+
marker of ``"may-change``, the marker lookup then missed, and every exit line
|
|
136
|
+
silently read as ``stable``.
|
|
137
|
+
|
|
138
|
+
Pulled out of ``render`` so the width rule has ONE home and a synthetic long
|
|
139
|
+
label can be tested against it directly, rather than only through whichever
|
|
140
|
+
labels happen to reach ``COLUMN`` today.
|
|
141
|
+
|
|
142
|
+
ONE SHAPE THIS CANNOT FIX, because the ambiguity is on the reader's side of
|
|
143
|
+
the line: a label whose LAST character is a space is indistinguishable from
|
|
144
|
+
the padding, so a reader stripping the padding returns it one character
|
|
145
|
+
short. Guaranteeing the separator does not help -- the space is inside the
|
|
146
|
+
label, before it. No label ends in a space today, and
|
|
147
|
+
``test_no_label_ends_in_a_space`` keeps it that way.
|
|
148
|
+
"""
|
|
149
|
+
width = max(COLUMN, len(label) + 1)
|
|
150
|
+
return f"{kind:<6}{label:<{width}}{marker}"
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def load_markers(path: Path) -> Markers:
|
|
154
|
+
"""(kind, spelling) -> 'may-change' | 'reserved', from a stability table.
|
|
155
|
+
|
|
156
|
+
An absent file is an empty table rather than an error: a checkout with no
|
|
157
|
+
stability table has made no promises, and every item reading ``stable``
|
|
158
|
+
there is the stated default. The entries are ``"<kind> <spelling>"``, split
|
|
159
|
+
on the FIRST space, because a spelling carries spaces of its own.
|
|
160
|
+
"""
|
|
161
|
+
if not path.exists():
|
|
162
|
+
return {}
|
|
163
|
+
data = tomllib.loads(path.read_text())
|
|
164
|
+
out: Markers = {}
|
|
165
|
+
for marker in ("may-change", "reserved"):
|
|
166
|
+
for entry in data.get(marker, []):
|
|
167
|
+
kind, _, spelling = entry.partition(" ")
|
|
168
|
+
out[(kind, spelling)] = marker
|
|
169
|
+
return out
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def plain_marker(kind: str) -> Callable[[str, Markers], str]:
|
|
173
|
+
"""The default marker rule for a kind: plain lookup, stable unless named.
|
|
174
|
+
|
|
175
|
+
Defaulting to stable is deliberate -- a new item is stable unless somebody
|
|
176
|
+
says otherwise in review -- and it is the rule most kinds want, so a
|
|
177
|
+
consumer registering one writes ``plain_marker("exit")`` rather than a
|
|
178
|
+
four-line closure that has to be read to be trusted.
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
def resolve(spelling: str, markers: Markers) -> str:
|
|
182
|
+
return markers.get((kind, spelling), "stable")
|
|
183
|
+
|
|
184
|
+
return resolve
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@dataclass(frozen=True)
|
|
188
|
+
class Kind:
|
|
189
|
+
"""One item kind, as the consumer registers it.
|
|
190
|
+
|
|
191
|
+
``items`` yields ``Row``s; ``marker`` answers for this kind alone, which is
|
|
192
|
+
the point of registering per kind -- the lookup rule for a ``json`` key path
|
|
193
|
+
(inherit the canonical verb's promise) is not the rule for an ``exit`` code
|
|
194
|
+
(plain lookup) and a single dispatching function had to know both.
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
name: str
|
|
198
|
+
items: Callable[[], list[Row]]
|
|
199
|
+
marker: Callable[[str, Markers], str]
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class Registry:
|
|
203
|
+
"""The kinds this dump carries, in the order the consumer registered them."""
|
|
204
|
+
|
|
205
|
+
def __init__(self) -> None:
|
|
206
|
+
self._kinds: list[Kind] = []
|
|
207
|
+
|
|
208
|
+
def register(
|
|
209
|
+
self,
|
|
210
|
+
name: str,
|
|
211
|
+
items: Callable[[], list[Row]],
|
|
212
|
+
marker: Callable[[str, Markers], str],
|
|
213
|
+
) -> Kind:
|
|
214
|
+
if any(kind.name == name for kind in self._kinds):
|
|
215
|
+
raise ValueError(f"kind {name!r} is already registered")
|
|
216
|
+
kind = Kind(name=name, items=items, marker=marker)
|
|
217
|
+
self._kinds.append(kind)
|
|
218
|
+
return kind
|
|
219
|
+
|
|
220
|
+
@property
|
|
221
|
+
def names(self) -> tuple[str, ...]:
|
|
222
|
+
return tuple(kind.name for kind in self._kinds)
|
|
223
|
+
|
|
224
|
+
def rows(self) -> list[Row]:
|
|
225
|
+
out: list[Row] = []
|
|
226
|
+
for kind in self._kinds:
|
|
227
|
+
out.extend(kind.items())
|
|
228
|
+
return out
|
|
229
|
+
|
|
230
|
+
def marker(self, kind: str, spelling: str, markers: Markers) -> str:
|
|
231
|
+
"""The marker for one item, from the kind that owns it.
|
|
232
|
+
|
|
233
|
+
An UNREGISTERED kind is refused rather than defaulted. Answering
|
|
234
|
+
``stable`` for a kind nobody registered would publish a promise no one
|
|
235
|
+
made, and it would do it silently -- the failure would look exactly like
|
|
236
|
+
an item the reviewers simply never named.
|
|
237
|
+
"""
|
|
238
|
+
for registered in self._kinds:
|
|
239
|
+
if registered.name == kind:
|
|
240
|
+
return registered.marker(spelling, markers)
|
|
241
|
+
raise KeyError(
|
|
242
|
+
f"no kind {kind!r} is registered; registered: {list(self.names)}"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def blocks(rows: list[Row]) -> list[tuple[str, list[Row]]]:
|
|
247
|
+
"""Rows grouped by command path, the groups in COMMAND-TREE order.
|
|
248
|
+
|
|
249
|
+
The ORDER is by path, which is what makes the file read as a tree: ``store``
|
|
250
|
+
before ``store init``, and both before ``store-x``, so a command's
|
|
251
|
+
subcommands sit with it rather than scattered by an unrelated key.
|
|
252
|
+
|
|
253
|
+
THE KEY IS WRITTEN ON THE PATH'S TOKENS, AND ON THIS DUMP THAT CHANGES
|
|
254
|
+
NOTHING -- measured, not assumed: the separator is a space (0x20), every
|
|
255
|
+
character a path component can begin with sorts above it, and token order and
|
|
256
|
+
joined-string order agree on all 88 real headings. It is written on tokens
|
|
257
|
+
because the RULE is what a reader auditing the order should find stated, and
|
|
258
|
+
citing a separator's incidental collation order as the reason is how a rule
|
|
259
|
+
becomes an accident nobody can check.
|
|
260
|
+
|
|
261
|
+
The UNGROUPED block sorts LAST, and THAT is the part the key actually earns:
|
|
262
|
+
it keys on the ABSENCE of tokens rather than on the empty string, which would
|
|
263
|
+
sort it first and open the file with the items that have no home.
|
|
264
|
+
"""
|
|
265
|
+
by_group: dict[str, list[Row]] = {}
|
|
266
|
+
for row in rows:
|
|
267
|
+
by_group.setdefault(row.group, []).append(row)
|
|
268
|
+
return sorted(
|
|
269
|
+
by_group.items(), key=lambda item: (not item[0].split(), item[0].split())
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def render(registry: Registry, markers: Markers | None = None) -> str:
|
|
274
|
+
"""The exact bytes of an ``.api`` file, terminated by one newline.
|
|
275
|
+
|
|
276
|
+
GROUPED BY COMMAND, not by kind. The first column is still the item kind and
|
|
277
|
+
each line still carries one item, but the ORDER is now the command tree: a
|
|
278
|
+
heading names a command and its items sit under it. The previous order was a
|
|
279
|
+
single flat lexicographic sort, so all 120 ``arg`` rows preceded all 215
|
|
280
|
+
``flag`` rows and no command's surface could be read in one place -- "what
|
|
281
|
+
does ``store init`` take" was four searches, one per kind.
|
|
282
|
+
|
|
283
|
+
WITHIN a block the rows are sorted by their rendered line, which is the old
|
|
284
|
+
(kind, then label) order: a diff then reads one command at a time, and an
|
|
285
|
+
item that moves shows up moving between two blocks rather than teleporting
|
|
286
|
+
to a different corner of the file.
|
|
287
|
+
|
|
288
|
+
The heading is a COMMENT (``# <command>``) rather than a row, so a reader
|
|
289
|
+
that parses item lines has one unambiguous shape to parse and the heading
|
|
290
|
+
cannot be mistaken for a kind. EVERY block gets one, the ungrouped block
|
|
291
|
+
included (``# (ungrouped)``): a heading is what separates one block from the
|
|
292
|
+
next, so the block without one is the block that gets misread.
|
|
293
|
+
"""
|
|
294
|
+
markers = {} if markers is None else markers
|
|
295
|
+
lines: list[str] = []
|
|
296
|
+
for group, rows in blocks(registry.rows()):
|
|
297
|
+
lines.append(f"# {group or UNGROUPED}")
|
|
298
|
+
lines += sorted(
|
|
299
|
+
line(
|
|
300
|
+
row.kind,
|
|
301
|
+
label(row.spelling, row.hidden, row.hidden_by),
|
|
302
|
+
registry.marker(row.kind, row.spelling, markers),
|
|
303
|
+
)
|
|
304
|
+
for row in rows
|
|
305
|
+
)
|
|
306
|
+
return "\n".join(lines) + "\n"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"required": ["schema_version", "workspace_name", "members"],
|
|
5
|
+
"properties": {
|
|
6
|
+
"schema_version": {"const": 1},
|
|
7
|
+
"workspace_name": {"type": "string", "minLength": 1},
|
|
8
|
+
"members": {"type": "array", "minItems": 1, "items": {"type": "object", "required": ["name", "path", "upstream", "ref", "pin", "mode", "remotes"], "properties": {"name": {"type": "string"}, "path": {"type": "string"}, "upstream": {"type": "string"}, "ref": {"type": "string"}, "pin": {"type": "string", "pattern": "^[0-9a-f]{40}$"}, "mode": {"const": "full"}, "remotes": {"type": "object", "required": ["origin"], "properties": {"origin": {"type": "string"}}}}}}
|
|
9
|
+
}
|
|
10
|
+
}
|