orbi-cli 0.7.2__tar.gz → 0.7.3__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.
- {orbi_cli-0.7.2/src/orbi_cli.egg-info → orbi_cli-0.7.3}/PKG-INFO +1 -1
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/pyproject.toml +1 -1
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/__init__.py +1 -1
- orbi_cli-0.7.3/src/orbi/blocked_comment.py +35 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/claim.py +13 -13
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/delivery_comment.py +44 -13
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/failure.py +4 -6
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/failure_report.py +51 -54
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/language.py +32 -6
- orbi_cli-0.7.3/src/orbi/language_terms.py +77 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/progress.py +32 -33
- orbi_cli-0.7.3/src/orbi/progress_display.py +111 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/progress_summary.py +5 -3
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/release.py +4 -6
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/review_merge.py +27 -13
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/runner.py +17 -16
- {orbi_cli-0.7.2 → orbi_cli-0.7.3/src/orbi_cli.egg-info}/PKG-INFO +1 -1
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi_cli.egg-info/SOURCES.txt +9 -0
- orbi_cli-0.7.3/tests/test_blocked_comment.py +174 -0
- orbi_cli-0.7.3/tests/test_bootstrap_adapters.py +466 -0
- orbi_cli-0.7.3/tests/test_bootstrap_delivery.py +6125 -0
- orbi_cli-0.7.3/tests/test_bootstrap_pi.py +2856 -0
- orbi_cli-0.7.3/tests/test_bootstrap_preflight.py +2162 -0
- orbi_cli-0.7.3/tests/test_bootstrap_release.py +4579 -0
- orbi_cli-0.7.3/tests/test_bootstrap_runner.py +7075 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_deliver_pr_fakes.py +1 -1
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_failure_report.py +44 -13
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_fix_needed_loop.py +38 -2
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_github_fakes.py +24 -2
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_message_language.py +186 -7
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_progress_wiring.py +104 -8
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_resume_pr.py +113 -12
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_review_merge.py +119 -9
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_run_id_e2e.py +2 -1
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_steering_review.py +1 -1
- orbi_cli-0.7.2/tests/test_bootstrap_runner.py +0 -24593
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/LICENSE +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/README.md +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/docs/licenses/sustainable-use-license.md +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/setup.cfg +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/branch_reclaim.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/checks.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/clarify.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/cli.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/cli_source.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/comment_kind.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/config.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/delivery_labels.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/delivery_scene.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/engine_source.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/example_config.toml +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/git_identity.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/git_transport.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/github.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/gitops.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/human_review.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/journal.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/launchd_deploy.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/merge_gate.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/merge_handoff.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/milestone.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/milestone_command.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/notify.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/pi_activity.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/pi_command.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/pi_process.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/pi_recovery.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/pi_session.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/pilot_setup.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/pilot_slots.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/progress_outcome.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/release_comments.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/release_git.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/release_notes.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/repo_config.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/review_rounds.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/run_state.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/runner_health.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/scene.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/scheduler.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/scheduler_units.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/session_command.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/source_pool.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/status_observe.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/suggest.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/systemd_deploy.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi/ticket_command.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi_cli.egg-info/dependency_links.txt +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi_cli.egg-info/entry_points.txt +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/src/orbi_cli.egg-info/top_level.txt +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_agents_md.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_apply_label_patch.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_branch_reclaim.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_ci_failure_issue_workflow.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_ci_failure_triage.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_ci_macos_workflow.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_ci_workflow.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_clarify.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_claude_plugin.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_cli_install.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_cli_milestone.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_cli_packaging.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_cli_source.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_comment_identity.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_comment_readers_contract.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_concurrency_e2e.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_constitution.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_coverage_gate.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_delivery_branch_delete.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_delivery_labels.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_delivery_scene.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_docker_docs.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_docker_entrypoint.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_docker_image_workflow.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_docs_i18n.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_docs_issue_shapes.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_docs_labels.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_docs_mermaid.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_docs_releases.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_docs_single_source.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_docs_site.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_engine_source.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_failure.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_gh_read_retry.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_git_base_smoke.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_git_merge_smoke.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_git_network_retry.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_git_transport.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_github.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_gitops.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_gitops_fakes.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_human_review.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_import_graph.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_install_sh.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_issue_1367.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_issue_461.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_issue_template_config.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_journal.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_journal_events.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_launchd_deploy.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_license.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_milestone_command.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_notify.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_ops_e2e.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_orbi.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_orbi_dashboard.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_orbi_exporter.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_patch_ratchet.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_pi_activity.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_pi_command.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_pi_extensions.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_pi_provider_templates.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_pi_recovery.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_pi_session.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_pilot_setup.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_pilot_slots.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_pr_issue_link_workflow.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_prerequisite_check.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_progress.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_prompt_contract.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_public_contracts.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_publish_workflow.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_release_docs_workflow.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_release_ticket_templates.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_repo_config.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_resume_continue_e2e.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_resume_e2e.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_run_artifacts.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_runner_config.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_runner_health.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_runner_source_freshness.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_scene.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_scheduler.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_size_ratchet.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_src_layout.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_status_observe.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_suggest.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_suite_hygiene.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_systemd_deploy.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_systemd_timer.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_ticket_command.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_user_acceptance_contract.py +0 -0
- {orbi_cli-0.7.2 → orbi_cli-0.7.3}/tests/test_worktree_reclaim.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: orbi-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: Orbi — local AI development worker: claims GitHub Issues, runs Pi in isolated worktrees, and delivers PRs (GitHub Issues + labels are the only state store).
|
|
5
5
|
Author: xqliu
|
|
6
6
|
License-Expression: AGPL-3.0-only OR LicenseRef-Sustainable-Use-1.0
|
|
@@ -44,7 +44,7 @@ build-backend = "setuptools.build_meta"
|
|
|
44
44
|
|
|
45
45
|
[project]
|
|
46
46
|
name = "orbi-cli"
|
|
47
|
-
version = "0.7.
|
|
47
|
+
version = "0.7.3"
|
|
48
48
|
description = "Orbi — local AI development worker: claims GitHub Issues, runs Pi in isolated worktrees, and delivers PRs (GitHub Issues + labels are the only state store)."
|
|
49
49
|
readme = "README.md"
|
|
50
50
|
requires-python = ">=3.14"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""The ONE exit for a comment that blocks an Issue (Issue #1730).
|
|
2
|
+
|
|
3
|
+
Ten scenes put an Issue into `ai-blocked` and each own their reason text,
|
|
4
|
+
unblock step, hidden markers and extra fields. What they must NOT each own is
|
|
5
|
+
the pointer to where a person can ask for help: it is appended here, once, so
|
|
6
|
+
a blocked scene added later through this exit carries the community help line
|
|
7
|
+
without anyone remembering to add it.
|
|
8
|
+
|
|
9
|
+
A comment that is not a blocked scene (a retrying diagnostic, progress, a
|
|
10
|
+
receipt) never goes through this exit and never gets the line.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from orbi import language
|
|
15
|
+
from orbi.delivery_labels import EVENT_BLOCKED
|
|
16
|
+
from orbi.github import apply_label_patch, comment_issue
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def post_blocked_comment(number: int, *, repo: str, body: str,
|
|
20
|
+
current_labels, language_code: str) -> None:
|
|
21
|
+
"""Block `number`: apply the `ai-blocked` label patch, then comment.
|
|
22
|
+
|
|
23
|
+
The order is part of the contract: the label transition lands first, the
|
|
24
|
+
comment second. The body is posted exactly as the scene rendered it,
|
|
25
|
+
followed by a blank line and the `help.community` line in the run's
|
|
26
|
+
language — added here and only here, so it appears exactly once.
|
|
27
|
+
"""
|
|
28
|
+
apply_label_patch(
|
|
29
|
+
number, repo=repo, event=EVENT_BLOCKED,
|
|
30
|
+
current_labels=current_labels,
|
|
31
|
+
)
|
|
32
|
+
comment_issue(
|
|
33
|
+
number, repo=repo,
|
|
34
|
+
body=f"{body}\n\n{language.text(language_code, 'help.community')}",
|
|
35
|
+
)
|
|
@@ -18,10 +18,11 @@ from typing import NamedTuple
|
|
|
18
18
|
|
|
19
19
|
from orbi import milestone as milestone_bookkeeping
|
|
20
20
|
from orbi import scene
|
|
21
|
+
from orbi.blocked_comment import post_blocked_comment
|
|
21
22
|
from orbi.checks import _classify_rollup, _render_check
|
|
22
23
|
from orbi.config import RunnerConfig
|
|
23
24
|
from orbi.delivery_labels import (
|
|
24
|
-
AWAITING_MERGE_LABEL, BLOCKED_LABEL, EPIC_LABEL,
|
|
25
|
+
AWAITING_MERGE_LABEL, BLOCKED_LABEL, EPIC_LABEL,
|
|
25
26
|
FIX_NEEDED_LABEL, IN_PROGRESS_LABEL, MERGED_LABEL, P0_LABEL,
|
|
26
27
|
PR_OPENED_LABEL, READY_LABEL, RELEASE_LABEL,
|
|
27
28
|
)
|
|
@@ -29,7 +30,7 @@ from orbi.delivery_scene import (
|
|
|
29
30
|
EXTERNAL_PR_MARKER, DeliveryScene, _issue_label_set, body_markers, classify,
|
|
30
31
|
)
|
|
31
32
|
from orbi.github import (
|
|
32
|
-
_verify_epic_complete,
|
|
33
|
+
_verify_epic_complete, close_issue,
|
|
33
34
|
comment_issue, issue_comments, issue_priority, issue_view,
|
|
34
35
|
latest_run_marker, list_issues, milestone_open_issue_count,
|
|
35
36
|
open_blocker_numbers, open_pr_for_branch, pr_comments,
|
|
@@ -644,7 +645,7 @@ def pick_resumable_delivery(
|
|
|
644
645
|
repo_dir: Path | None = None, *, hooks: ResumeHooks,
|
|
645
646
|
language_code: str,
|
|
646
647
|
) -> tuple[dict, dict] | None:
|
|
647
|
-
"""Return the
|
|
648
|
+
"""Return the oldest FREE opened-PR delivery and its resume scene.
|
|
648
649
|
|
|
649
650
|
All opened-PR states are scanned: `ai-fix-needed`
|
|
650
651
|
(awaiting the next review session after a finding or a base
|
|
@@ -691,12 +692,13 @@ def pick_resumable_delivery(
|
|
|
691
692
|
excluded: `main` took it before the claim scan and holds it for the
|
|
692
693
|
whole delivery.
|
|
693
694
|
|
|
694
|
-
The query page is `max_concurrency + 1` candidates
|
|
695
|
+
The query page is `max_concurrency + 1` candidates, ordered OLDEST
|
|
696
|
+
Issue first (`sort:created-asc`, Issue #1734). At most
|
|
695
697
|
`max_concurrency` deliveries can be held by live co-runners, so a
|
|
696
698
|
free candidate is inside the page when one exists; one whose OPEN PR
|
|
697
|
-
still waits for CI (Issue #1473) is skipped too. The scan reviews
|
|
698
|
-
|
|
699
|
-
read — an in-flight delivery is never touched).
|
|
699
|
+
still waits for CI (Issue #1473) is skipped too. The scan reviews
|
|
700
|
+
the oldest FREE candidate (held ones are skipped before any
|
|
701
|
+
candidate read — an in-flight delivery is never touched).
|
|
700
702
|
"""
|
|
701
703
|
held = slot_held_deliveries(slot_dir, max_concurrency)
|
|
702
704
|
# `label:a,b` is GitHub's OR within one label qualifier
|
|
@@ -716,7 +718,7 @@ def pick_resumable_delivery(
|
|
|
716
718
|
search=(
|
|
717
719
|
f"label:{FIX_NEEDED_LABEL},{PR_OPENED_LABEL},"
|
|
718
720
|
f"{AWAITING_MERGE_LABEL} "
|
|
719
|
-
f"-label:{BLOCKED_LABEL} -label:{MERGED_LABEL}"
|
|
721
|
+
f"-label:{BLOCKED_LABEL} -label:{MERGED_LABEL} sort:created-asc"
|
|
720
722
|
),
|
|
721
723
|
json_fields="number,title,state,url,labels,body",
|
|
722
724
|
limit=max_concurrency + 1,
|
|
@@ -795,16 +797,14 @@ def pick_resumable_delivery(
|
|
|
795
797
|
LOGGER.error("issue=%s resume scene is missing: %s", number, exc)
|
|
796
798
|
marker = latest_run_marker(comments)
|
|
797
799
|
try:
|
|
798
|
-
|
|
799
|
-
number, repo=repo, event=EVENT_BLOCKED,
|
|
800
|
-
current_labels={FIX_NEEDED_LABEL},
|
|
801
|
-
)
|
|
802
|
-
comment_issue(
|
|
800
|
+
post_blocked_comment(
|
|
803
801
|
number, repo=repo,
|
|
804
802
|
body=(f"{marker}\n" if marker else "")
|
|
805
803
|
+ language.text(
|
|
806
804
|
language_code, "claim.scene_missing", error=exc,
|
|
807
805
|
),
|
|
806
|
+
current_labels={FIX_NEEDED_LABEL},
|
|
807
|
+
language_code=language_code,
|
|
808
808
|
)
|
|
809
809
|
except Exception:
|
|
810
810
|
LOGGER.exception("issue=%s failure reporting failed", number)
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
"""The runner-owned run comments
|
|
2
|
-
(Issue #1660)
|
|
1
|
+
"""The runner-owned run comments and the PR body's footer: the
|
|
2
|
+
started-Pi and opened-PR scenes (Issue #1660) and the attribution footer
|
|
3
|
+
(Issue #1729).
|
|
3
4
|
|
|
4
5
|
Both comments double as the run's machine-readable anchors: the started
|
|
5
6
|
comment carries the `started` kind marker and the opened-PR comment
|
|
6
|
-
carries the `orbi:scene:v1` block the next tick resumes from.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
carries the `orbi:scene:v1` block the next tick resumes from. Their
|
|
8
|
+
human text — the headline, the field labels and the stored display
|
|
9
|
+
values — follows the run's bound message language (the explicit
|
|
10
|
+
`language_code`); the hidden markers, the public machine rows
|
|
11
|
+
(`run_id=`, `repo_config:`) and the scene block stay exactly as they
|
|
12
|
+
were.
|
|
10
13
|
|
|
11
14
|
Extracted from `runner.py` unchanged apart from the language, so the
|
|
12
15
|
frozen runner module does not grow (Issue #1229) and a caller keeps
|
|
@@ -19,7 +22,16 @@ from orbi import language, scene
|
|
|
19
22
|
from orbi.comment_kind import COMMENT_KIND_STARTED
|
|
20
23
|
from orbi.delivery_scene import RunContext
|
|
21
24
|
from orbi.journal import validate_run_id
|
|
22
|
-
from orbi.progress import
|
|
25
|
+
from orbi.progress import (
|
|
26
|
+
STARTED_HEADER,
|
|
27
|
+
_run_info_fields,
|
|
28
|
+
field_block,
|
|
29
|
+
)
|
|
30
|
+
from orbi.progress_display import (
|
|
31
|
+
field_label,
|
|
32
|
+
localized_fields,
|
|
33
|
+
vocabulary,
|
|
34
|
+
)
|
|
23
35
|
|
|
24
36
|
|
|
25
37
|
def started_pi_comment_body(ctx: RunContext, run_info: str,
|
|
@@ -38,20 +50,38 @@ def started_pi_comment_body(ctx: RunContext, run_info: str,
|
|
|
38
50
|
if extra_fields:
|
|
39
51
|
fields.update(extra_fields)
|
|
40
52
|
info = _run_info_fields(run_info)
|
|
53
|
+
rendered_info = {
|
|
54
|
+
key: (vocabulary(language_code, "priority", info[key])
|
|
55
|
+
if key == "priority" else info[key])
|
|
56
|
+
for key in ("run_id", "priority")
|
|
57
|
+
if key in info
|
|
58
|
+
}
|
|
41
59
|
headline = language.text(
|
|
42
60
|
language_code, "delivery.started_headline",
|
|
43
61
|
) + " " + " ".join(
|
|
44
|
-
f"{key}={
|
|
45
|
-
if key in info
|
|
62
|
+
f"{key}={value}" for key, value in rendered_info.items()
|
|
46
63
|
)
|
|
64
|
+
detail_keys = {"base_sha", "repo_config", "branch", "session"}
|
|
47
65
|
return field_block(
|
|
48
|
-
ctx.run_id, headline, fields,
|
|
49
|
-
detail_keys={
|
|
66
|
+
ctx.run_id, headline, localized_fields(language_code, fields),
|
|
67
|
+
detail_keys={field_label(language_code, key) for key in detail_keys},
|
|
50
68
|
kind=COMMENT_KIND_STARTED,
|
|
51
69
|
language_code=language_code,
|
|
52
70
|
)
|
|
53
71
|
|
|
54
72
|
|
|
73
|
+
def pr_attribution_footer(issue: int, attribution_link: str, *,
|
|
74
|
+
language_code: str) -> str:
|
|
75
|
+
"""The PR body's attribution footer in the bound message language.
|
|
76
|
+
|
|
77
|
+
The link URL and `#<issue>` are the machine facts and stay verbatim.
|
|
78
|
+
"""
|
|
79
|
+
return language.text(
|
|
80
|
+
language_code, "runner.pr_attribution",
|
|
81
|
+
link=attribution_link, issue=issue,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
55
85
|
def opened_pr_comment_body(run_id: str, run_info: str, pr_url: str,
|
|
56
86
|
external: bool = False, *,
|
|
57
87
|
language_code: str,
|
|
@@ -86,9 +116,10 @@ def opened_pr_comment_body(run_id: str, run_info: str, pr_url: str,
|
|
|
86
116
|
pr_url=pr_url,
|
|
87
117
|
external="true" if external else "",
|
|
88
118
|
)
|
|
119
|
+
detail_keys = {"base_sha", "repo_config", "branch", "session"}
|
|
89
120
|
body = field_block(
|
|
90
|
-
run_id, headline, fields,
|
|
91
|
-
detail_keys={
|
|
121
|
+
run_id, headline, localized_fields(language_code, fields),
|
|
122
|
+
detail_keys={field_label(language_code, key) for key in detail_keys},
|
|
92
123
|
language_code=language_code,
|
|
93
124
|
)
|
|
94
125
|
lines = body.splitlines()
|
|
@@ -529,9 +529,7 @@ def _failure_comment_body(*, outcome: str, action: str, reason: str,
|
|
|
529
529
|
if evidence:
|
|
530
530
|
details.append(evidence.lstrip())
|
|
531
531
|
details.append("</details>")
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
# last paragraph is the community help line (Issue #1684).
|
|
537
|
-
return f"{body}\n\n{message('help.community')}"
|
|
532
|
+
# The blocked diagnostic's community help line is NOT added here: the
|
|
533
|
+
# ONE blocked exit appends it to every blocked comment (Issue #1730),
|
|
534
|
+
# so this renderer and that exit can never both add it.
|
|
535
|
+
return "\n\n".join(parts + ["\n".join(details)])
|
|
@@ -27,12 +27,13 @@ import re
|
|
|
27
27
|
from pathlib import Path
|
|
28
28
|
|
|
29
29
|
from orbi import failure, language, runner_health, scene
|
|
30
|
+
from orbi.blocked_comment import post_blocked_comment
|
|
30
31
|
from orbi.comment_kind import (
|
|
31
32
|
COMMENT_KIND_FAILURE,
|
|
32
33
|
comment_kind_marker,
|
|
33
34
|
)
|
|
34
35
|
from orbi.delivery_labels import (
|
|
35
|
-
|
|
36
|
+
EVENT_FIX_NEEDED, FIX_NEEDED_LABEL, IN_PROGRESS_LABEL,
|
|
36
37
|
)
|
|
37
38
|
from orbi.failure import (
|
|
38
39
|
_failure_comment_body, _failure_detail, _failure_summary,
|
|
@@ -488,10 +489,6 @@ def block_scene_failure(issue: dict, error: ValueError, repo: str,
|
|
|
488
489
|
)
|
|
489
490
|
marker = latest_run_marker(comments)
|
|
490
491
|
try:
|
|
491
|
-
apply_label_patch(
|
|
492
|
-
number, repo=repo, event=EVENT_BLOCKED,
|
|
493
|
-
current_labels={FIX_NEEDED_LABEL},
|
|
494
|
-
)
|
|
495
492
|
# A scene that cannot be recovered is an external
|
|
496
493
|
# precondition the AI cannot fix by itself (the runner cannot
|
|
497
494
|
# derive run_id, branch, worktree or PR without the trusted
|
|
@@ -499,7 +496,7 @@ def block_scene_failure(issue: dict, error: ValueError, repo: str,
|
|
|
499
496
|
# comment states the EXPLICIT reason why automatic recovery is
|
|
500
497
|
# impossible plus the human next step (restore the scene comment,
|
|
501
498
|
# then the ONE shared unblock step of the catalog, Issue #1724).
|
|
502
|
-
|
|
499
|
+
post_blocked_comment(
|
|
503
500
|
number, repo=repo,
|
|
504
501
|
body=(
|
|
505
502
|
(f"{marker}\n"
|
|
@@ -508,6 +505,8 @@ def block_scene_failure(issue: dict, error: ValueError, repo: str,
|
|
|
508
505
|
) + language.text(
|
|
509
506
|
language_code, "failure.scene_blocked", error=error,
|
|
510
507
|
),
|
|
508
|
+
current_labels={FIX_NEEDED_LABEL},
|
|
509
|
+
language_code=language_code,
|
|
511
510
|
)
|
|
512
511
|
except Exception:
|
|
513
512
|
LOGGER.exception("issue=%s failure reporting failed", number)
|
|
@@ -529,11 +528,7 @@ def block_repo_config_failure(number: int, source_repo: str,
|
|
|
529
528
|
cleanly (`main` releases the slot in its `finally`).
|
|
530
529
|
"""
|
|
531
530
|
try:
|
|
532
|
-
|
|
533
|
-
number, repo=source_repo, event=EVENT_BLOCKED,
|
|
534
|
-
current_labels=set(current_labels),
|
|
535
|
-
)
|
|
536
|
-
comment_issue(
|
|
531
|
+
post_blocked_comment(
|
|
537
532
|
number, repo=source_repo,
|
|
538
533
|
body=(
|
|
539
534
|
f"{run_marker(run_id)}\n"
|
|
@@ -542,6 +537,8 @@ def block_repo_config_failure(number: int, source_repo: str,
|
|
|
542
537
|
language_code, "failure.repo_config_blocked", error=error,
|
|
543
538
|
)
|
|
544
539
|
),
|
|
540
|
+
current_labels=set(current_labels),
|
|
541
|
+
language_code=language_code,
|
|
545
542
|
)
|
|
546
543
|
except Exception:
|
|
547
544
|
LOGGER.exception("issue=%s repo_config_failure_report_failed", number)
|
|
@@ -600,14 +597,10 @@ def _report_resume_failure(*, number: int, source_repo: str, run_id: str,
|
|
|
600
597
|
verified: continuing on a guessed identity risks a
|
|
601
598
|
silent fresh redo on top of unknown work, and a fresh run would
|
|
602
599
|
lose the existing work. The Issue goes `ai-blocked` ALONE (the
|
|
603
|
-
claim label removed) with the exact reason
|
|
604
|
-
|
|
600
|
+
claim label removed) with the exact reason — a human decides (restore
|
|
601
|
+
or remove the worktree, relabel).
|
|
605
602
|
"""
|
|
606
|
-
|
|
607
|
-
number, repo=source_repo, event=EVENT_BLOCKED,
|
|
608
|
-
current_labels={IN_PROGRESS_LABEL},
|
|
609
|
-
)
|
|
610
|
-
comment_issue(
|
|
603
|
+
post_blocked_comment(
|
|
611
604
|
number, repo=source_repo,
|
|
612
605
|
body=(
|
|
613
606
|
f"{run_marker(run_id)}\n"
|
|
@@ -616,8 +609,10 @@ def _report_resume_failure(*, number: int, source_repo: str, run_id: str,
|
|
|
616
609
|
language_code, "failure.resume_blocked",
|
|
617
610
|
detail=_redact_local_paths(_failure_detail(error)),
|
|
618
611
|
run_id=run_id,
|
|
619
|
-
)
|
|
612
|
+
)
|
|
620
613
|
),
|
|
614
|
+
current_labels={IN_PROGRESS_LABEL},
|
|
615
|
+
language_code=language_code,
|
|
621
616
|
)
|
|
622
617
|
|
|
623
618
|
|
|
@@ -804,52 +799,47 @@ def report_delivery_failure(
|
|
|
804
799
|
action if workflow_refusal and action
|
|
805
800
|
else _failure_summary(reason, language_code)
|
|
806
801
|
)
|
|
802
|
+
scene = scene_line() or "- run: `-`"
|
|
807
803
|
if blocked:
|
|
808
|
-
apply_label_patch(
|
|
809
|
-
number, repo=source_repo, event=EVENT_BLOCKED,
|
|
810
|
-
current_labels=labels,
|
|
811
|
-
)
|
|
812
|
-
scene = scene_line() or "- run: `-`"
|
|
813
|
-
body = _failure_comment_body(
|
|
814
|
-
outcome="blocked", action=action, reason=reason,
|
|
815
|
-
diagnosis=diagnosis, scene=scene, evidence=evidence_detail,
|
|
816
|
-
pr_url=pr_url, issue=issue_context(source_repo, number),
|
|
817
|
-
run_id=run_id or "-", failure_record=failure_record,
|
|
818
|
-
language_code=language_code,
|
|
819
|
-
)
|
|
820
|
-
if classify:
|
|
821
|
-
body = body.replace(
|
|
822
|
-
"</details>",
|
|
823
|
-
f"\n{language.text(language_code, _BLOCKED_PRECONDITION_PHRASE_KEY)}\n</details>",
|
|
824
|
-
1,
|
|
825
|
-
)
|
|
826
|
-
if blocked_suffix:
|
|
827
|
-
body = body.replace(
|
|
828
|
-
"</details>",
|
|
829
|
-
f"\n{blocked_suffix.lstrip('; ')}\n</details>", 1,
|
|
830
|
-
)
|
|
831
804
|
outcome = "blocked"
|
|
832
805
|
else:
|
|
806
|
+
# The recoverable scene is the only one that rewrites labels here:
|
|
807
|
+
# the blocked exit owns its own patch (Issue #1730).
|
|
833
808
|
apply_label_patch(
|
|
834
809
|
number, repo=source_repo, event=EVENT_FIX_NEEDED,
|
|
835
810
|
current_labels=labels,
|
|
836
811
|
)
|
|
837
|
-
scene = scene_line() or "- run: `-`"
|
|
838
812
|
if review_scene_block is not None:
|
|
839
813
|
scene += f"\n{_redact_local_paths(review_scene_block)}"
|
|
840
|
-
body = _failure_comment_body(
|
|
841
|
-
outcome="fix needed", action=action, reason=reason,
|
|
842
|
-
diagnosis=diagnosis, scene=scene, evidence=evidence_detail,
|
|
843
|
-
pr_url=pr_url, issue=issue_context(source_repo, number),
|
|
844
|
-
run_id=run_id or "-", failure_record=failure_record,
|
|
845
|
-
language_code=language_code,
|
|
846
|
-
)
|
|
847
814
|
outcome = "fix needed"
|
|
815
|
+
body = _failure_comment_body(
|
|
816
|
+
outcome="blocked" if blocked else "fix needed",
|
|
817
|
+
action=action, reason=reason, diagnosis=diagnosis, scene=scene,
|
|
818
|
+
evidence=evidence_detail, pr_url=pr_url,
|
|
819
|
+
issue=issue_context(source_repo, number), run_id=run_id or "-",
|
|
820
|
+
failure_record=failure_record, language_code=language_code,
|
|
821
|
+
)
|
|
822
|
+
if blocked and classify:
|
|
823
|
+
body = body.replace(
|
|
824
|
+
"</details>",
|
|
825
|
+
f"\n{language.text(language_code, _BLOCKED_PRECONDITION_PHRASE_KEY)}\n</details>",
|
|
826
|
+
1,
|
|
827
|
+
)
|
|
828
|
+
if blocked and blocked_suffix:
|
|
829
|
+
body = body.replace(
|
|
830
|
+
"</details>",
|
|
831
|
+
f"\n{blocked_suffix.lstrip('; ')}\n</details>", 1,
|
|
832
|
+
)
|
|
848
833
|
# An oversized comment shrinks ONLY its evidence segments, never its
|
|
849
834
|
# structure: a cut lands inside the last fence, so the closer and the
|
|
850
|
-
# help line render as code in the collapsed block (Issue #1711).
|
|
835
|
+
# help line render as code in the collapsed block (Issue #1711). The
|
|
836
|
+
# help line the blocked exit appends is inside that budget too (#1730).
|
|
837
|
+
help_suffix = (
|
|
838
|
+
f"\n\n{language.text(language_code, 'help.community')}" if blocked else ""
|
|
839
|
+
)
|
|
851
840
|
cap = EVIDENCE_SEGMENT_CAP
|
|
852
|
-
while evidence_detail and cap
|
|
841
|
+
while (evidence_detail and cap
|
|
842
|
+
and len(body) + len(help_suffix) > FAILURE_COMMENT_MAX_CHARS):
|
|
853
843
|
cap = max(0, cap - _EVIDENCE_CAP_STEP)
|
|
854
844
|
shrunk = _failure_evidence(
|
|
855
845
|
worktree, exc, language_code=language_code, cap=cap)
|
|
@@ -928,9 +918,16 @@ def report_delivery_failure(
|
|
|
928
918
|
"failure_comment_deduplicated", issue=number,
|
|
929
919
|
run_id=run_id, fingerprint=fingerprint,
|
|
930
920
|
)
|
|
931
|
-
if post_new_comment:
|
|
921
|
+
if post_new_comment and blocked:
|
|
922
|
+
# The blocked exit owns the label patch and the help line (Issue
|
|
923
|
+
# #1730): the label lands first, the comment second.
|
|
924
|
+
post_blocked_comment(
|
|
925
|
+
number, repo=source_repo, body=body,
|
|
926
|
+
current_labels=labels, language_code=language_code,
|
|
927
|
+
)
|
|
928
|
+
elif post_new_comment:
|
|
932
929
|
comment_issue(number, repo=source_repo, body=body)
|
|
933
|
-
if pr_url
|
|
930
|
+
if pr_url:
|
|
934
931
|
# The recoverable scene is written to the PR too:
|
|
935
932
|
# the next review session and any human watcher see it where
|
|
936
933
|
# the delivery lives. A blocked Issue is terminal — Issue only.
|
|
@@ -37,6 +37,8 @@ import os
|
|
|
37
37
|
import re
|
|
38
38
|
from typing import Mapping
|
|
39
39
|
|
|
40
|
+
from orbi.language_terms import DISPLAY_TERMS
|
|
41
|
+
|
|
40
42
|
LANGUAGE_EN = "en"
|
|
41
43
|
LANGUAGE_ZH = "zh"
|
|
42
44
|
LANGUAGES = (LANGUAGE_EN, LANGUAGE_ZH)
|
|
@@ -220,7 +222,7 @@ MESSAGES: dict[str, tuple[str, str]] = {
|
|
|
220
222
|
"review.findings_heading": ("### Findings", "### 评审发现"),
|
|
221
223
|
"review.findings_milestone": ("review findings: round {round}, {blockers} blocker(s), {majors} major(s) for PR #{pr_number}", "评审发现:第 {round} 轮,PR #{pr_number} 有 {blockers} 个 blocker、{majors} 个 major"),
|
|
222
224
|
"review.findings_outcome": ("**Orbi review findings**\n\nround {round}: {blockers} blocker(s), {majors} major(s); the next review session retries the same PR automatically", "**Orbi 评审发现**\n\n第 {round} 轮:{blockers} 个 blocker、{majors} 个 major;下一个评审 session 会自动重试同一个 PR"),
|
|
223
|
-
"review.delivered_outcome": ("**Orbi delivered**\n\nPR {pr_url} merged (merge_commit={merge_commit} review_rounds={round})", "**Orbi 已交付**\n\nPR {pr_url}
|
|
225
|
+
"review.delivered_outcome": ("**Orbi delivered**\n\nPR {pr_url} merged (merge_commit={merge_commit} review_rounds={round})", "**Orbi 已交付**\n\nPR {pr_url} 已合并(合并提交 {merge_commit},共 {round} 个评审轮次)"),
|
|
224
226
|
"review.base_advance_heading": ("Orbi base advance retry {round} for PR #{pr_number}:", "Orbi base 同步重试第 {round} 次,PR #{pr_number}:"),
|
|
225
227
|
"review.gate_ci_blocked": ("CI merge gate blocked: {message} (run_id={run_id})", "CI 合并门禁被阻塞:{message}(run_id={run_id})"),
|
|
226
228
|
"review.gate_blocked": ("Merge gate blocked: {message} (run_id={run_id})", "合并门禁被阻塞:{message}(run_id={run_id})"),
|
|
@@ -289,6 +291,7 @@ MESSAGES: dict[str, tuple[str, str]] = {
|
|
|
289
291
|
"progress.header": ("**Orbi progress**", "**Orbi 进度**"),
|
|
290
292
|
"progress.run_details": ("Run details", "运行详情"),
|
|
291
293
|
"progress.milestone": ("Orbi:", "Orbi:"),
|
|
294
|
+
"progress.milestone_heading": ("{prefix} {headline}", "{prefix}{headline}"),
|
|
292
295
|
"progress.field.role": ("role", "角色"),
|
|
293
296
|
"progress.field.step": ("step", "阶段"),
|
|
294
297
|
"progress.field.reported_at": ("reported at", "报告时间"),
|
|
@@ -580,6 +583,10 @@ MESSAGES: dict[str, tuple[str, str]] = {
|
|
|
580
583
|
"The delivery stopped before it could be completed: {detail}",
|
|
581
584
|
"交付在完成之前停止了:{detail}",
|
|
582
585
|
),
|
|
586
|
+
"runner.pr_attribution": (
|
|
587
|
+
"Built by [Orbi]({link}) from Issue #{issue}",
|
|
588
|
+
"由 [Orbi]({link}) 构建,基于 Issue #{issue}",
|
|
589
|
+
),
|
|
583
590
|
"runner.pr_closed_milestone": ("blocked: PR {pr_url} was closed without a merge; the delivery is terminally failed", "已阻塞:PR {pr_url} 未合并就被关闭;本次交付最终失败"),
|
|
584
591
|
# The standalone terminal failure comment of a closed PR (the
|
|
585
592
|
# milestone above and the progress scene carry the same fact).
|
|
@@ -635,9 +642,12 @@ MESSAGES: dict[str, tuple[str, str]] = {
|
|
|
635
642
|
),
|
|
636
643
|
# The implement-phase finish: the PR was OPENED, not merged, so this
|
|
637
644
|
# is its own key (`review.delivered_outcome` is the merge outcome).
|
|
645
|
+
# It names the wait that follows (checks and the review session),
|
|
646
|
+
# never a wait duration: the tick interval is each deployment's own
|
|
647
|
+
# setting (Issue #1733).
|
|
638
648
|
"delivery.outcome_pr_opened": (
|
|
639
|
-
"**Orbi
|
|
640
|
-
"**Orbi
|
|
649
|
+
"**Orbi opened the PR: waiting for checks and review**",
|
|
650
|
+
"**Orbi 已打开 PR:等待检查与评审**",
|
|
641
651
|
),
|
|
642
652
|
"delivery.recoverable_outcome": (
|
|
643
653
|
"**Orbi {recoverable_name}**\n\n"
|
|
@@ -665,7 +675,7 @@ MESSAGES: dict[str, tuple[str, str]] = {
|
|
|
665
675
|
),
|
|
666
676
|
"review.summary_human_action": ("maintainer-actionable merge retry", "需要维护者处理的合并重试"),
|
|
667
677
|
"review.summary_pass_recorded": ("pass recorded on {head}; merged after CI", "在 {head} 上记录通过;CI 之后合并"),
|
|
668
|
-
"review.summary_pass_clean": ("pass, no findings", "
|
|
678
|
+
"review.summary_pass_clean": ("pass, no findings", "通过,没有发现问题"),
|
|
669
679
|
"review.summary_pass_fixed": ("pass, {findings} findings fixed in-session", "通过,会话内修复了 {findings} 项发现"),
|
|
670
680
|
|
|
671
681
|
# Delivery failures (blocked / fix needed).
|
|
@@ -841,12 +851,12 @@ MESSAGES: dict[str, tuple[str, str]] = {
|
|
|
841
851
|
),
|
|
842
852
|
"clarify.repair_outcome": (
|
|
843
853
|
"Outcome: what a user does, and what they should see differently.",
|
|
844
|
-
"
|
|
854
|
+
"结果:用户要做什么,以及应该看到什么变化。",
|
|
845
855
|
),
|
|
846
856
|
"clarify.repair_acceptance": (
|
|
847
857
|
"Acceptance: how a finished delivery is told apart from an "
|
|
848
858
|
"unfinished one.",
|
|
849
|
-
"
|
|
859
|
+
"验收:如何判断交付已完成、而不是半成品。",
|
|
850
860
|
),
|
|
851
861
|
"clarify.followup": (
|
|
852
862
|
"Orbi re-checks the ticket when it claims it and starts the "
|
|
@@ -881,6 +891,10 @@ MESSAGES: dict[str, tuple[str, str]] = {
|
|
|
881
891
|
),
|
|
882
892
|
}
|
|
883
893
|
|
|
894
|
+
# The display vocabulary of the run comments lives in its own module to
|
|
895
|
+
# keep this catalog owner under the size cap (Issue #1729).
|
|
896
|
+
MESSAGES.update(DISPLAY_TERMS)
|
|
897
|
+
|
|
884
898
|
|
|
885
899
|
UNBLOCK_STEP_FIELD = "unblock_step"
|
|
886
900
|
|
|
@@ -905,6 +919,18 @@ def text(language: str, key: str, **fields: object) -> str:
|
|
|
905
919
|
return template.format(**fields) if fields else template
|
|
906
920
|
|
|
907
921
|
|
|
922
|
+
def term(language: str, key: str, default: str) -> str:
|
|
923
|
+
"""One catalogue term, or `default` when the key has no entry.
|
|
924
|
+
|
|
925
|
+
Used for display vocabulary whose raw value must survive when the
|
|
926
|
+
catalogue does not know it (an external tool name, a future phase):
|
|
927
|
+
the caller keeps the raw value instead of inventing a translation.
|
|
928
|
+
"""
|
|
929
|
+
if key not in MESSAGES:
|
|
930
|
+
return default
|
|
931
|
+
return text(language, key)
|
|
932
|
+
|
|
933
|
+
|
|
908
934
|
# --- the model-session boundary -------------------------------------------
|
|
909
935
|
#
|
|
910
936
|
# The instruction is appended to the rendered system prompt AT THE SESSION
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""The display vocabulary of Orbi's run comments (Issue #1729).
|
|
2
|
+
|
|
3
|
+
Split out of `language.MESSAGES` so the catalog owner stays under the
|
|
4
|
+
size ratchet's 1,000-line cap. Every key here is a display label or a
|
|
5
|
+
stored display value; `language.MESSAGES` merges this map in place, so
|
|
6
|
+
`language.text()` / `language.term()` remain the ONE reading point. No
|
|
7
|
+
machine row (run marker, `run_id=`, `repo_config:`, `failure:`, receipt
|
|
8
|
+
rows) has an entry here.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
DISPLAY_TERMS: dict[str, tuple[str, str]] = {
|
|
12
|
+
# Display labels of the started / opened-PR scene field blocks. The
|
|
13
|
+
# machine rows (`run_id=`, `repo_config:`) have no entry here and stay
|
|
14
|
+
# verbatim (Issue #1729).
|
|
15
|
+
"progress.field.base_branch": ("base_branch", "基础分支"),
|
|
16
|
+
"progress.field.base_sha": ("base_sha", "基础 SHA"),
|
|
17
|
+
"progress.field.task_type": ("task_type", "任务类型"),
|
|
18
|
+
"progress.field.external": ("external", "外部"),
|
|
19
|
+
"progress.field.repo_config_ignored": (
|
|
20
|
+
"repo_config_ignored", "忽略的仓库配置键",
|
|
21
|
+
),
|
|
22
|
+
"progress.field.repo_config_changed": (
|
|
23
|
+
"repo_config_changed", "仓库配置变更",
|
|
24
|
+
),
|
|
25
|
+
"progress.field.repo_config_diff": ("repo_config_diff", "仓库配置差异"),
|
|
26
|
+
# Display labels of the merged milestone's visible rows.
|
|
27
|
+
"progress.field.result": ("result", "结果"),
|
|
28
|
+
"progress.field.merge_commit": ("merge_commit", "合并提交"),
|
|
29
|
+
"progress.field.review_rounds": ("review_rounds", "评审轮次"),
|
|
30
|
+
"progress.field.external_commits": ("external_commits", "外部提交"),
|
|
31
|
+
"progress.field.commits": ("commits", "提交数"),
|
|
32
|
+
# The display vocabulary of the progress values (Issue #1729): the
|
|
33
|
+
# stored value stays the raw machine word, the rendered one follows
|
|
34
|
+
# the message language. An unknown value renders verbatim.
|
|
35
|
+
"progress.role.implement": ("implement", "实现"),
|
|
36
|
+
"progress.role.review": ("review", "评审"),
|
|
37
|
+
"progress.role.release": ("release", "发布"),
|
|
38
|
+
"progress.role.ticket": ("ticket", "工单"),
|
|
39
|
+
"progress.role.suggest": ("suggest", "建议"),
|
|
40
|
+
"progress.step.starting": ("starting", "启动中"),
|
|
41
|
+
"progress.step.implement": ("implement", "实现"),
|
|
42
|
+
"progress.step.review": ("review", "评审"),
|
|
43
|
+
"progress.step.checks": ("checks", "检查"),
|
|
44
|
+
"progress.step.merge": ("merge", "合并"),
|
|
45
|
+
"progress.step.release": ("release", "发布"),
|
|
46
|
+
"progress.priority.p0": ("p0", "P0"),
|
|
47
|
+
"progress.priority.normal": ("normal", "普通"),
|
|
48
|
+
"progress.tests.passed": ("passed", "通过"),
|
|
49
|
+
"progress.tests.pending": ("pending", "等待中"),
|
|
50
|
+
"progress.tests.failed": ("failed: {names}", "失败:{names}"),
|
|
51
|
+
"progress.activity.read": ("read", "读取"),
|
|
52
|
+
"progress.activity.edit": ("edit", "编辑"),
|
|
53
|
+
"progress.activity.command": ("command", "命令"),
|
|
54
|
+
"progress.activity.analysis": ("analysis", "分析"),
|
|
55
|
+
"progress.phase.starting": ("starting", "启动中"),
|
|
56
|
+
"progress.phase.session_pending": ("session_pending", "等待会话"),
|
|
57
|
+
"progress.phase.request_pending": ("request_pending", "等待响应"),
|
|
58
|
+
"progress.phase.read": ("read", "读取"),
|
|
59
|
+
"progress.phase.edit": ("edit", "编辑"),
|
|
60
|
+
"progress.phase.write": ("write", "写入"),
|
|
61
|
+
"progress.phase.bash": ("bash", "命令行"),
|
|
62
|
+
"progress.phase.pr": ("pr", "PR"),
|
|
63
|
+
"progress.phase.push": ("push", "推送"),
|
|
64
|
+
"progress.phase.commit": ("commit", "提交"),
|
|
65
|
+
"progress.phase.base": ("base", "基线"),
|
|
66
|
+
"progress.phase.worktree": ("worktree", "工作树"),
|
|
67
|
+
"progress.phase.ui": ("ui", "界面"),
|
|
68
|
+
"progress.task_type.ticket-only": ("ticket-only", "仅工单"),
|
|
69
|
+
"progress.task_type.ops": ("ops", "运维"),
|
|
70
|
+
"progress.boolean.true": ("true", "是"),
|
|
71
|
+
"progress.boolean.false": ("false", "否"),
|
|
72
|
+
# The merged milestone's display headline. It is the ONLY milestone
|
|
73
|
+
# whose event word arrives raw: every other milestone text is already
|
|
74
|
+
# a localized sentence from `language.text`, so an entry for its
|
|
75
|
+
# English word would never be reached (Issue #1729).
|
|
76
|
+
"progress.milestone_headline.merged": ("merged", "已合并"),
|
|
77
|
+
}
|