@opengsd/gsd-path 1.6.2 → 1.7.0
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.
- package/CHANGELOG.md +21 -0
- package/DOCS.md +27 -1
- package/FULL.md +2 -1
- package/HOOKS.md +38 -4
- package/QUICK.md +1 -1
- package/README.md +19 -11
- package/RUNTIME.md +5 -3
- package/WORKFLOW.md +9 -2
- package/package.json +1 -1
- package/scripts/_common.py +76 -2
- package/scripts/archive_milestone.py +129 -7
- package/scripts/build_recovery.py +25 -2
- package/scripts/build_state.py +333 -8
- package/scripts/check_handoffs.py +437 -30
- package/scripts/check_task_briefs.py +135 -11
- package/scripts/discussion_validate.py +46 -10
- package/scripts/dispatch_driver.py +28 -3
- package/scripts/git_guard.py +44 -4
- package/scripts/guard_hook.py +456 -9
- package/scripts/install.py +34 -4
- package/scripts/integration.py +196 -39
- package/scripts/isolation.py +996 -38
- package/scripts/lean_verification.py +28 -13
- package/scripts/loop_run.py +9 -4
- package/scripts/members.py +17 -3
- package/scripts/pipeline_diagnose.py +104 -1
- package/scripts/pipeline_state.py +10 -2
- package/scripts/runtime_store.py +88 -3
- package/scripts/state_checkpoint.py +15 -0
- package/scripts/status_runtime.py +73 -2
- package/scripts/workflow_run.py +10 -3
- package/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path/BUILD.md +3 -1
- package/skills/gsd-path/DEFINE.md +2 -1
- package/skills/gsd-path/PLAN.md +10 -0
- package/skills/gsd-path/SHIP.md +41 -8
- package/skills/gsd-path/SKILL.md +16 -1
- package/skills/gsd-path/references/build-native.md +20 -3
- package/skills/gsd-path/references/coder.md +6 -3
- package/skills/gsd-path/references/reviewer-final.md +10 -0
- package/skills/gsd-path/references/reviewer-wave.md +61 -22
- package/skills/gsd-path/references/reviewer.md +8 -6
- package/skills/gsd-path/references/runtime-selection.md +36 -0
- package/skills/gsd-path/references/spec-probes.md +19 -1
- package/skills/gsd-path/scripts/_common.py +76 -2
- package/skills/gsd-path/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path/scripts/build_recovery.py +25 -2
- package/skills/gsd-path/scripts/build_state.py +333 -8
- package/skills/gsd-path/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path/scripts/dispatch_driver.py +28 -3
- package/skills/gsd-path/scripts/git_guard.py +44 -4
- package/skills/gsd-path/scripts/integration.py +196 -39
- package/skills/gsd-path/scripts/isolation.py +996 -38
- package/skills/gsd-path/scripts/lean_verification.py +28 -13
- package/skills/gsd-path/scripts/loop_run.py +9 -4
- package/skills/gsd-path/scripts/members.py +17 -3
- package/skills/gsd-path/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path/scripts/status_runtime.py +73 -2
- package/skills/gsd-path/scripts/workflow_run.py +10 -3
- package/skills/gsd-path/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path/templates/intent.md +5 -3
- package/skills/gsd-path/templates/task.md +8 -1
- package/skills/gsd-path/templates/wave-review.md +24 -8
- package/skills/gsd-path-build/SKILL.md +3 -1
- package/skills/gsd-path-build/references/build-native.md +20 -3
- package/skills/gsd-path-build/references/coder.md +6 -3
- package/skills/gsd-path-build/references/reviewer-final.md +10 -0
- package/skills/gsd-path-build/references/reviewer-wave.md +61 -22
- package/skills/gsd-path-build/references/reviewer.md +8 -6
- package/skills/gsd-path-build/references/runtime-selection.md +36 -0
- package/skills/gsd-path-build/references/spec-probes.md +19 -1
- package/skills/gsd-path-build/scripts/_common.py +76 -2
- package/skills/gsd-path-build/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-build/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-build/scripts/build_state.py +333 -8
- package/skills/gsd-path-build/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-build/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-build/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-build/scripts/dispatch_driver.py +28 -3
- package/skills/gsd-path-build/scripts/integration.py +196 -39
- package/skills/gsd-path-build/scripts/isolation.py +996 -38
- package/skills/gsd-path-build/scripts/loop_run.py +9 -4
- package/skills/gsd-path-build/scripts/members.py +17 -3
- package/skills/gsd-path-build/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-build/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-build/scripts/workflow_run.py +10 -3
- package/skills/gsd-path-build/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-build/templates/task.md +8 -1
- package/skills/gsd-path-build/templates/wave-review.md +24 -8
- package/skills/gsd-path-decide/references/runtime-selection.md +36 -0
- package/skills/gsd-path-decide/scripts/_common.py +76 -2
- package/skills/gsd-path-decide/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-decide/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-decide/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-decide/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-decide/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-decide/scripts/integration.py +196 -39
- package/skills/gsd-path-decide/scripts/isolation.py +996 -38
- package/skills/gsd-path-decide/scripts/loop_run.py +9 -4
- package/skills/gsd-path-decide/scripts/members.py +17 -3
- package/skills/gsd-path-decide/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-decide/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-decide/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-define/SKILL.md +2 -1
- package/skills/gsd-path-define/references/runtime-selection.md +36 -0
- package/skills/gsd-path-define/references/spec-probes.md +19 -1
- package/skills/gsd-path-define/scripts/_common.py +76 -2
- package/skills/gsd-path-define/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-define/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-define/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-define/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-define/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-define/scripts/integration.py +196 -39
- package/skills/gsd-path-define/scripts/isolation.py +996 -38
- package/skills/gsd-path-define/scripts/loop_run.py +9 -4
- package/skills/gsd-path-define/scripts/members.py +17 -3
- package/skills/gsd-path-define/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-define/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-define/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-define/templates/intent.md +5 -3
- package/skills/gsd-path-discuss/references/runtime-selection.md +36 -0
- package/skills/gsd-path-discuss/scripts/_common.py +76 -2
- package/skills/gsd-path-discuss/scripts/loop_run.py +9 -4
- package/skills/gsd-path-discuss/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-docs-audit/references/runtime-selection.md +36 -0
- package/skills/gsd-path-docs-audit/scripts/_common.py +76 -2
- package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-docs-audit/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-docs-audit/scripts/integration.py +196 -39
- package/skills/gsd-path-docs-audit/scripts/isolation.py +996 -38
- package/skills/gsd-path-docs-audit/scripts/loop_run.py +9 -4
- package/skills/gsd-path-docs-audit/scripts/members.py +17 -3
- package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-forensics/references/runtime-selection.md +36 -0
- package/skills/gsd-path-forensics/scripts/_common.py +76 -2
- package/skills/gsd-path-forensics/scripts/loop_run.py +9 -4
- package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +104 -1
- package/skills/gsd-path-forensics/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-inspect/references/runtime-selection.md +36 -0
- package/skills/gsd-path-inspect/scripts/_common.py +76 -2
- package/skills/gsd-path-inspect/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-inspect/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-inspect/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-inspect/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-inspect/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-inspect/scripts/integration.py +196 -39
- package/skills/gsd-path-inspect/scripts/isolation.py +996 -38
- package/skills/gsd-path-inspect/scripts/loop_run.py +9 -4
- package/skills/gsd-path-inspect/scripts/members.py +17 -3
- package/skills/gsd-path-inspect/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-inspect/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-inspect/scripts/workflow_run.py +10 -3
- package/skills/gsd-path-inspect/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-loop/references/runtime-selection.md +36 -0
- package/skills/gsd-path-loop/scripts/_common.py +76 -2
- package/skills/gsd-path-loop/scripts/loop_run.py +9 -4
- package/skills/gsd-path-loop/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-migrate/references/runtime-selection.md +36 -0
- package/skills/gsd-path-migrate/scripts/_common.py +76 -2
- package/skills/gsd-path-migrate/scripts/loop_run.py +9 -4
- package/skills/gsd-path-plan/SKILL.md +10 -0
- package/skills/gsd-path-plan/references/runtime-selection.md +36 -0
- package/skills/gsd-path-plan/references/spec-probes.md +19 -1
- package/skills/gsd-path-plan/scripts/_common.py +76 -2
- package/skills/gsd-path-plan/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-plan/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-plan/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-plan/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-plan/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-plan/scripts/integration.py +196 -39
- package/skills/gsd-path-plan/scripts/isolation.py +996 -38
- package/skills/gsd-path-plan/scripts/loop_run.py +9 -4
- package/skills/gsd-path-plan/scripts/members.py +17 -3
- package/skills/gsd-path-plan/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-plan/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-plan/scripts/workflow_run.py +10 -3
- package/skills/gsd-path-plan/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-plan/templates/task.md +8 -1
- package/skills/gsd-path-research/references/runtime-selection.md +36 -0
- package/skills/gsd-path-research/scripts/_common.py +76 -2
- package/skills/gsd-path-research/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-research/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-research/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-research/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-research/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-research/scripts/integration.py +196 -39
- package/skills/gsd-path-research/scripts/isolation.py +996 -38
- package/skills/gsd-path-research/scripts/loop_run.py +9 -4
- package/skills/gsd-path-research/scripts/members.py +17 -3
- package/skills/gsd-path-research/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-research/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-research/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-roadmap/references/runtime-selection.md +36 -0
- package/skills/gsd-path-roadmap/scripts/_common.py +76 -2
- package/skills/gsd-path-roadmap/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-roadmap/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-roadmap/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-roadmap/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-roadmap/scripts/integration.py +196 -39
- package/skills/gsd-path-roadmap/scripts/isolation.py +996 -38
- package/skills/gsd-path-roadmap/scripts/loop_run.py +9 -4
- package/skills/gsd-path-roadmap/scripts/members.py +17 -3
- package/skills/gsd-path-roadmap/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-roadmap/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-ship/SKILL.md +41 -8
- package/skills/gsd-path-ship/references/reviewer-final.md +10 -0
- package/skills/gsd-path-ship/references/reviewer-wave.md +61 -22
- package/skills/gsd-path-ship/references/reviewer.md +8 -6
- package/skills/gsd-path-ship/references/runtime-selection.md +36 -0
- package/skills/gsd-path-ship/references/spec-probes.md +19 -1
- package/skills/gsd-path-ship/scripts/_common.py +76 -2
- package/skills/gsd-path-ship/scripts/archive_milestone.py +129 -7
- package/skills/gsd-path-ship/scripts/build_recovery.py +25 -2
- package/skills/gsd-path-ship/scripts/build_state.py +333 -8
- package/skills/gsd-path-ship/scripts/check_handoffs.py +437 -30
- package/skills/gsd-path-ship/scripts/check_task_briefs.py +135 -11
- package/skills/gsd-path-ship/scripts/discussion_validate.py +46 -10
- package/skills/gsd-path-ship/scripts/integration.py +196 -39
- package/skills/gsd-path-ship/scripts/isolation.py +996 -38
- package/skills/gsd-path-ship/scripts/lean_verification.py +28 -13
- package/skills/gsd-path-ship/scripts/loop_run.py +9 -4
- package/skills/gsd-path-ship/scripts/members.py +17 -3
- package/skills/gsd-path-ship/scripts/pipeline_state.py +10 -2
- package/skills/gsd-path-ship/scripts/state_checkpoint.py +15 -0
- package/skills/gsd-path-ship/scripts/workflow_run.py +10 -3
- package/skills/gsd-path-ship/scripts/worktree_paths.py +45 -0
- package/skills/gsd-path-ship/templates/wave-review.md +24 -8
- package/skills/gsd-path-undo/references/runtime-selection.md +36 -0
- package/skills/gsd-path-undo/scripts/_common.py +76 -2
- package/skills/gsd-path-undo/scripts/loop_run.py +9 -4
- package/skills/gsd-path-undo/scripts/worktree_paths.py +45 -0
- package/skills/path/BUILD.md +3 -1
- package/skills/path/DEFINE.md +2 -1
- package/skills/path/PLAN.md +10 -0
- package/skills/path/SHIP.md +41 -8
- package/skills/path/SKILL.md +16 -1
- package/skills/path/references/build-native.md +20 -3
- package/skills/path/references/coder.md +6 -3
- package/skills/path/references/reviewer-final.md +10 -0
- package/skills/path/references/reviewer-wave.md +61 -22
- package/skills/path/references/reviewer.md +8 -6
- package/skills/path/references/runtime-selection.md +36 -0
- package/skills/path/references/spec-probes.md +19 -1
- package/skills/path/scripts/_common.py +76 -2
- package/skills/path/scripts/archive_milestone.py +129 -7
- package/skills/path/scripts/build_recovery.py +25 -2
- package/skills/path/scripts/build_state.py +333 -8
- package/skills/path/scripts/check_handoffs.py +437 -30
- package/skills/path/scripts/check_task_briefs.py +135 -11
- package/skills/path/scripts/discussion_validate.py +46 -10
- package/skills/path/scripts/dispatch_driver.py +28 -3
- package/skills/path/scripts/git_guard.py +44 -4
- package/skills/path/scripts/integration.py +196 -39
- package/skills/path/scripts/isolation.py +996 -38
- package/skills/path/scripts/lean_verification.py +28 -13
- package/skills/path/scripts/loop_run.py +9 -4
- package/skills/path/scripts/members.py +17 -3
- package/skills/path/scripts/pipeline_state.py +10 -2
- package/skills/path/scripts/state_checkpoint.py +15 -0
- package/skills/path/scripts/status_runtime.py +73 -2
- package/skills/path/scripts/workflow_run.py +10 -3
- package/skills/path/scripts/worktree_paths.py +45 -0
- package/skills/path/templates/intent.md +5 -3
- package/skills/path/templates/task.md +8 -1
- package/skills/path/templates/wave-review.md +24 -8
|
@@ -781,7 +781,14 @@ def validate_panel_skip_receipt(path: Path, expected_mode: str) -> None:
|
|
|
781
781
|
not isinstance(item, dict)
|
|
782
782
|
or set(item) != {"family", "reason"}
|
|
783
783
|
or item["family"] not in KNOWN_FAMILIES
|
|
784
|
-
|
|
784
|
+
# review_panel resolve writes every one of these reasons (#460);
|
|
785
|
+
# the receipt must validate whatever the helper can produce.
|
|
786
|
+
or item["reason"] not in {
|
|
787
|
+
"parent family",
|
|
788
|
+
"not advertised",
|
|
789
|
+
"cap",
|
|
790
|
+
"excluded reviewer family",
|
|
791
|
+
}
|
|
785
792
|
for item in skipped
|
|
786
793
|
):
|
|
787
794
|
raise ArchiveError(f"{path.name} has invalid skipped families")
|
|
@@ -1998,6 +2005,62 @@ def member_ship_rows(project: Path, archive: Path, ship_commit: str) -> list:
|
|
|
1998
2005
|
return rows
|
|
1999
2006
|
|
|
2000
2007
|
|
|
2008
|
+
def tag_metadata_fields(project: Path, tag_ref: str) -> dict[str, str]:
|
|
2009
|
+
"""The Key: value fields of an annotated milestone tag's message."""
|
|
2010
|
+
message = require_git_success(
|
|
2011
|
+
run_git(project, "for-each-ref", "--format=%(contents)", tag_ref),
|
|
2012
|
+
"inspect milestone tag",
|
|
2013
|
+
)
|
|
2014
|
+
fields: dict[str, str] = {}
|
|
2015
|
+
for line in message.splitlines():
|
|
2016
|
+
match = re.fullmatch(r"(Mode|Pull-Request|Ship|Landing): (.+)", line)
|
|
2017
|
+
if match:
|
|
2018
|
+
if match.group(1) in fields:
|
|
2019
|
+
raise ArchiveError("milestone tag repeats metadata")
|
|
2020
|
+
fields[match.group(1)] = match.group(2)
|
|
2021
|
+
return fields
|
|
2022
|
+
|
|
2023
|
+
|
|
2024
|
+
def accept_rebased_ship(
|
|
2025
|
+
project: Path, archive: Path, ship_commit: str, reviewed_head: str
|
|
2026
|
+
) -> Optional[str]:
|
|
2027
|
+
"""Accept the rebased ship copy a rebase merge landed, keyed by the milestone tag.
|
|
2028
|
+
|
|
2029
|
+
Historical validation only (issue #450): after a rebase merge the landed
|
|
2030
|
+
ship copy's parent is never the reviewed HEAD, so the strict check cannot
|
|
2031
|
+
pass over landed history. Acceptance requires the milestone tag to name
|
|
2032
|
+
exactly this commit as its Landing, the original ship commit the tag
|
|
2033
|
+
names to still resolve, the strict invariant re-proven against the
|
|
2034
|
+
original ship's parent, and the rebase replay to hold. Returns the tag's
|
|
2035
|
+
original Ship sha, or None when the landing is not a proven rebase: a
|
|
2036
|
+
landing whose originals no longer resolve anywhere is refused, because
|
|
2037
|
+
the per-task landing proofs elsewhere in validation pin the original
|
|
2038
|
+
commits and a tag receipt alone cannot carry a full validation.
|
|
2039
|
+
"""
|
|
2040
|
+
tag_ref = f"refs/tags/milestone/{archive.name}"
|
|
2041
|
+
if run_git(project, "rev-parse", "--verify", "--quiet", tag_ref).returncode != 0:
|
|
2042
|
+
return None
|
|
2043
|
+
try:
|
|
2044
|
+
fields = tag_metadata_fields(project, tag_ref)
|
|
2045
|
+
except ArchiveError:
|
|
2046
|
+
return None
|
|
2047
|
+
if fields.get("Mode") != "pull-request":
|
|
2048
|
+
return None
|
|
2049
|
+
original = fields.get("Ship")
|
|
2050
|
+
if not original or fields.get("Landing") != ship_commit:
|
|
2051
|
+
return None
|
|
2052
|
+
if run_git(project, "cat-file", "-e", f"{original}^{{commit}}").returncode != 0:
|
|
2053
|
+
return None
|
|
2054
|
+
original_parent = require_git_success(
|
|
2055
|
+
run_git(project, "rev-parse", f"{original}^"), "resolve original ship parent"
|
|
2056
|
+
)
|
|
2057
|
+
if not reviewed_head_covers(project, reviewed_head, original_parent):
|
|
2058
|
+
return None
|
|
2059
|
+
if _common.rebase_landing_problem(project, original, ship_commit) is not None:
|
|
2060
|
+
return None
|
|
2061
|
+
return original
|
|
2062
|
+
|
|
2063
|
+
|
|
2001
2064
|
def validate(repo: Path, *, historical: bool = False) -> dict:
|
|
2002
2065
|
project, _, state, configured, archive, archived_files, reviewed_head = prepared_transaction(
|
|
2003
2066
|
repo,
|
|
@@ -2093,11 +2156,17 @@ def validate(repo: Path, *, historical: bool = False) -> dict:
|
|
|
2093
2156
|
|
|
2094
2157
|
members = member_ship_rows(project, archive, ship_commit)
|
|
2095
2158
|
ship_parent = require_git_success(run_git(project, "rev-parse", f"{ship_commit}^"), "resolve ship parent")
|
|
2159
|
+
rebased_ship: Optional[str] = None
|
|
2096
2160
|
if not reviewed_head_covers(project, reviewed_head, ship_parent):
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2161
|
+
rebased_ship = (
|
|
2162
|
+
accept_rebased_ship(project, archive, ship_commit, reviewed_head)
|
|
2163
|
+
if historical else None
|
|
2100
2164
|
)
|
|
2165
|
+
if rebased_ship is None:
|
|
2166
|
+
raise ArchiveError(
|
|
2167
|
+
f"FINAL.md Reviewed HEAD {reviewed_head} does not match ship parent {ship_parent}; "
|
|
2168
|
+
f"{_common.RUNTIME_PIN_ALLOWANCE}"
|
|
2169
|
+
)
|
|
2101
2170
|
require_canonical_commit_body(
|
|
2102
2171
|
project,
|
|
2103
2172
|
ship_commit,
|
|
@@ -2141,6 +2210,8 @@ def validate(repo: Path, *, historical: bool = False) -> dict:
|
|
|
2141
2210
|
)
|
|
2142
2211
|
|
|
2143
2212
|
result = {"archive": configured, "commit": ship_commit}
|
|
2213
|
+
if rebased_ship is not None:
|
|
2214
|
+
result["rebased_ship"] = rebased_ship
|
|
2144
2215
|
if members:
|
|
2145
2216
|
result["members"] = members
|
|
2146
2217
|
return result
|
|
@@ -2178,6 +2249,15 @@ def parser() -> argparse.ArgumentParser:
|
|
|
2178
2249
|
)
|
|
2179
2250
|
validate_integrated_parser.add_argument("--repo", required=True, type=Path)
|
|
2180
2251
|
validate_integrated_parser.add_argument("--slug", required=True)
|
|
2252
|
+
validate_integrated_parser.add_argument(
|
|
2253
|
+
"--historical",
|
|
2254
|
+
action="store_true",
|
|
2255
|
+
help=(
|
|
2256
|
+
"validate from the landed default branch after the milestone closed "
|
|
2257
|
+
"(the bound branch may be gone; a rebase-merged landing is proven "
|
|
2258
|
+
"through the milestone tag)"
|
|
2259
|
+
),
|
|
2260
|
+
)
|
|
2181
2261
|
|
|
2182
2262
|
refresh_parser = subparsers.add_parser(
|
|
2183
2263
|
"refresh-origin",
|
|
@@ -2190,6 +2270,17 @@ def parser() -> argparse.ArgumentParser:
|
|
|
2190
2270
|
help="after preflight, integrate each locked member in lock order before the ship commit",
|
|
2191
2271
|
)
|
|
2192
2272
|
close_members_parser.add_argument("--repo", required=True, type=Path)
|
|
2273
|
+
close_members_parser.add_argument(
|
|
2274
|
+
"--adopt-landed-member",
|
|
2275
|
+
action="append",
|
|
2276
|
+
default=[],
|
|
2277
|
+
metavar="MEMBER=SHA",
|
|
2278
|
+
help=(
|
|
2279
|
+
"user-approved recovery for a member whose reviewed HEAD already reached its "
|
|
2280
|
+
"default branch outside Path; MEMBER=SHA names the live default-branch tip "
|
|
2281
|
+
"to adopt as the landing"
|
|
2282
|
+
),
|
|
2283
|
+
)
|
|
2193
2284
|
|
|
2194
2285
|
integrate_parser = subparsers.add_parser(
|
|
2195
2286
|
"integrate",
|
|
@@ -2197,6 +2288,16 @@ def parser() -> argparse.ArgumentParser:
|
|
|
2197
2288
|
)
|
|
2198
2289
|
integrate_parser.add_argument("--repo", required=True, type=Path)
|
|
2199
2290
|
integrate_parser.add_argument("--slug", required=True)
|
|
2291
|
+
integrate_parser.add_argument(
|
|
2292
|
+
"--adopt-published-bound",
|
|
2293
|
+
metavar="SHA",
|
|
2294
|
+
help=(
|
|
2295
|
+
"user-approved recovery when origin's bound branch is a strict ancestor "
|
|
2296
|
+
"of the ship commit (a build task pushed it as a deploy pin, #464); "
|
|
2297
|
+
"the 40-hex SHA must equal the live remote tip exactly; the publish "
|
|
2298
|
+
"fast-forwards under the existing exact lease. Direct integration only"
|
|
2299
|
+
),
|
|
2300
|
+
)
|
|
2200
2301
|
|
|
2201
2302
|
abandon_parser = subparsers.add_parser(
|
|
2202
2303
|
"abandon",
|
|
@@ -2225,13 +2326,34 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
2225
2326
|
elif arguments.command == "abandon":
|
|
2226
2327
|
result = abandon(arguments.repo, arguments.slug, arguments.reason)
|
|
2227
2328
|
elif arguments.command == "validate-integrated":
|
|
2228
|
-
result = integration.validate_integrated(
|
|
2329
|
+
result = integration.validate_integrated(
|
|
2330
|
+
arguments.repo, arguments.slug, historical=arguments.historical
|
|
2331
|
+
)
|
|
2229
2332
|
elif arguments.command == "refresh-origin":
|
|
2230
2333
|
result = integration.refresh_origin(arguments.repo)
|
|
2231
2334
|
elif arguments.command == "close-members":
|
|
2232
|
-
|
|
2335
|
+
adopt: dict[str, str] = {}
|
|
2336
|
+
for item in arguments.adopt_landed_member:
|
|
2337
|
+
name, separator, sha = item.partition("=")
|
|
2338
|
+
if not name or not separator or not re.fullmatch(r"[0-9a-f]{40}", sha):
|
|
2339
|
+
parser_error = (
|
|
2340
|
+
f"--adopt-landed-member expects MEMBER=SHA (40-hex), got {item!r}"
|
|
2341
|
+
)
|
|
2342
|
+
print(parser_error, file=sys.stderr)
|
|
2343
|
+
return 2
|
|
2344
|
+
adopt[name] = sha
|
|
2345
|
+
result = integration.close_members(arguments.repo, adopt=adopt or None)
|
|
2233
2346
|
elif arguments.command == "integrate":
|
|
2234
|
-
|
|
2347
|
+
adopt_bound = arguments.adopt_published_bound
|
|
2348
|
+
if adopt_bound is not None:
|
|
2349
|
+
adopt_bound = adopt_bound.lower()
|
|
2350
|
+
if adopt_bound is not None and not re.fullmatch(r"[0-9a-f]{40}", adopt_bound):
|
|
2351
|
+
print(
|
|
2352
|
+
f"--adopt-published-bound expects a 40-hex SHA, got {adopt_bound!r}",
|
|
2353
|
+
file=sys.stderr,
|
|
2354
|
+
)
|
|
2355
|
+
return 2
|
|
2356
|
+
result = integration.integrate(arguments.repo, arguments.slug, adopt_bound)
|
|
2235
2357
|
else:
|
|
2236
2358
|
result = validate(arguments.repo)
|
|
2237
2359
|
except (ArchiveError, OSError) as error:
|
|
@@ -144,7 +144,13 @@ def settled_tasks(repo: Path) -> dict[str, tuple[str, dict]]:
|
|
|
144
144
|
raise state.PipelineStateError(f"invalid recovery task path: {path.name}")
|
|
145
145
|
text = state._read_real_file(path, path.name)
|
|
146
146
|
fields = task_fields(text, path.name)
|
|
147
|
-
if fields.get("status")
|
|
147
|
+
if fields.get("status") == "withdrawn":
|
|
148
|
+
# A cited tombstone is settled inventory; recovery skips it (#483).
|
|
149
|
+
if not state._common.withdrawn_citations(text):
|
|
150
|
+
raise state.PipelineStateError(
|
|
151
|
+
f"withdrawn task lacks an owner citation: {path.name}"
|
|
152
|
+
)
|
|
153
|
+
elif fields.get("status") not in {"pending", "done"}:
|
|
148
154
|
raise state.PipelineStateError(f"settle task ownership before recovery: {path.name}")
|
|
149
155
|
if fields["status"] == "pending" and any(
|
|
150
156
|
fields.get(key) != "null" for key in ("agent", "base", "worktree", "task_branch")
|
|
@@ -250,7 +256,24 @@ def validate_review_backup(repo: Path, recovery: dict, directory: Path) -> None:
|
|
|
250
256
|
for path in directory.rglob("*")
|
|
251
257
|
if path.is_file() and not state._common.is_ignored_junk(path)} if directory.exists() else {}
|
|
252
258
|
if actual != expected:
|
|
253
|
-
|
|
259
|
+
extra = sorted(set(actual) - set(expected))
|
|
260
|
+
changed = sorted(
|
|
261
|
+
name for name in set(actual) & set(expected) if actual[name] != expected[name]
|
|
262
|
+
)
|
|
263
|
+
missing = sorted(set(expected) - set(actual))
|
|
264
|
+
detail = ""
|
|
265
|
+
if extra:
|
|
266
|
+
detail += f" files in the review backup that the recovery base does not have: {', '.join(extra)}."
|
|
267
|
+
if changed:
|
|
268
|
+
detail += f" files differing from the recovery base: {', '.join(changed)}."
|
|
269
|
+
if missing:
|
|
270
|
+
detail += f" files at the recovery base missing from the review backup: {', '.join(missing)}."
|
|
271
|
+
raise state.PipelineStateError(
|
|
272
|
+
"review backup differs from the recovery base." + detail
|
|
273
|
+
+ " prepare-build-recovery must run before re-planning touches any recovery"
|
|
274
|
+
" artifact; remove the extra files and restore the missing or changed ones"
|
|
275
|
+
" to the recovery base's content, then rerun prepare"
|
|
276
|
+
)
|
|
254
277
|
|
|
255
278
|
|
|
256
279
|
def prepare(repo: Path) -> dict:
|