feat(continuation): formalize continuation proofs for claim and push (closes #189)
Extend #188 continuation mode with issue claim status evidence, force-with-lease push reporting, canonical secret sweep helper, and composite final-report checks wired into assess_issue_selection_final_report.
This commit is contained in:
+117
-1
@@ -1046,6 +1046,8 @@ HANDOFF_ROLE_FIELDS = {
|
||||
("Continuation mode", ("continuation mode", "continuation")),
|
||||
("Existing PR", ("existing pr", "pr number")),
|
||||
("PR author", ("pr author", "existing pr author")),
|
||||
("Issue claim status", ("issue claim", "claim status",
|
||||
"status:in-progress")),
|
||||
("Branch", ("branch", "existing branch")),
|
||||
("Old PR head", ("old pr head", "old head")),
|
||||
("New PR head", ("new pr head", "new head")),
|
||||
@@ -1054,6 +1056,13 @@ HANDOFF_ROLE_FIELDS = {
|
||||
),
|
||||
}
|
||||
|
||||
# Canonical secret/provenance sweep for comparable continuation runs (#189).
|
||||
CANONICAL_SECRET_SWEEP_COMMAND = (
|
||||
"git diff prgs/master...HEAD | rg -i "
|
||||
"'(token|password|secret|api[_-]?key|authorization:)'"
|
||||
)
|
||||
CANONICAL_SECRET_SWEEP_SCOPE = "full feature-branch diff against prgs/master"
|
||||
|
||||
|
||||
def _handoff_section_lines(report_text):
|
||||
"""Return the lines of the Controller Handoff section, or None."""
|
||||
@@ -1657,18 +1666,89 @@ def assess_fresh_issue_selection(classifications: list[dict] | None) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def canonical_secret_sweep_report(*, clean: bool) -> dict:
|
||||
"""Return a sweep report using the canonical command/scope (#189)."""
|
||||
return {
|
||||
"method": CANONICAL_SECRET_SWEEP_COMMAND,
|
||||
"scope": CANONICAL_SECRET_SWEEP_SCOPE,
|
||||
"clean": clean,
|
||||
}
|
||||
|
||||
|
||||
def assess_force_with_lease_push_report(
|
||||
report_text: str,
|
||||
*,
|
||||
old_remote_head: str | None = None,
|
||||
expected_lease_head: str | None = None,
|
||||
new_pushed_head: str | None = None,
|
||||
branch_pushed: str | None = None,
|
||||
used_force_with_lease: bool | None = None,
|
||||
) -> dict:
|
||||
"""Issue #189: force-with-lease pushes must disclose lease evidence."""
|
||||
if not used_force_with_lease:
|
||||
return {"complete": True, "downgraded": False, "reasons": []}
|
||||
|
||||
text = report_text or ""
|
||||
lower = text.lower()
|
||||
reasons = []
|
||||
|
||||
if "force-with-lease" not in lower and "force with lease" not in lower:
|
||||
reasons.append(
|
||||
"continuation push used force-with-lease but report does not say so"
|
||||
)
|
||||
|
||||
for label, sha in (
|
||||
("old remote", old_remote_head),
|
||||
("lease", expected_lease_head),
|
||||
("pushed", new_pushed_head),
|
||||
):
|
||||
if not sha:
|
||||
reasons.append(f"force-with-lease proof missing {label} head SHA")
|
||||
elif not _FULL_SHA.match(sha.lower()):
|
||||
reasons.append(
|
||||
f"force-with-lease {label} head SHA is not a full 40-hex SHA"
|
||||
)
|
||||
elif sha.lower() not in lower:
|
||||
reasons.append(
|
||||
f"continuation report missing {label} head SHA in evidence"
|
||||
)
|
||||
|
||||
if branch_pushed:
|
||||
if branch_pushed.lower() not in lower:
|
||||
reasons.append("continuation report missing pushed branch name")
|
||||
only_branch_tokens = (
|
||||
"only feature branch",
|
||||
"push branch only",
|
||||
"only the feature branch",
|
||||
"only pushed feature branch",
|
||||
)
|
||||
if not any(t in lower for t in only_branch_tokens):
|
||||
reasons.append(
|
||||
"continuation report missing confirmation that only the "
|
||||
"feature branch was pushed"
|
||||
)
|
||||
|
||||
return {
|
||||
"complete": not reasons,
|
||||
"downgraded": bool(reasons),
|
||||
"reasons": reasons,
|
||||
}
|
||||
|
||||
|
||||
def assess_continuation_mode_report(
|
||||
report_text: str,
|
||||
*,
|
||||
pr_number: int | None = None,
|
||||
pr_author: str | None = None,
|
||||
issue_number: int | None = None,
|
||||
issue_claim_status: str | None = None,
|
||||
branch: str | None = None,
|
||||
old_head_sha: str | None = None,
|
||||
new_head_sha: str | None = None,
|
||||
session_authored_pr: bool | None = None,
|
||||
continuation_allowed_reason: str | None = None,
|
||||
) -> dict:
|
||||
"""Issue #188: continuation mode must disclose full PR evidence."""
|
||||
"""Issue #188/#189: continuation mode must disclose full PR evidence."""
|
||||
text = report_text or ""
|
||||
lower = text.lower()
|
||||
reasons = []
|
||||
@@ -1681,6 +1761,23 @@ def assess_continuation_mode_report(
|
||||
reasons.append(f"continuation report missing PR #{pr_number}")
|
||||
if pr_author and pr_author.lower() not in lower:
|
||||
reasons.append("continuation report missing PR author")
|
||||
if issue_number is not None:
|
||||
if (
|
||||
f"#{issue_number}" not in lower
|
||||
and f"issue #{issue_number}" not in lower
|
||||
and f"issue {issue_number}" not in lower
|
||||
):
|
||||
reasons.append(f"continuation report missing issue #{issue_number}")
|
||||
if issue_claim_status:
|
||||
claim_lower = issue_claim_status.lower()
|
||||
claim_tokens = (
|
||||
claim_lower,
|
||||
"claim status",
|
||||
"issue claim",
|
||||
"status:in-progress",
|
||||
)
|
||||
if not any(t in lower for t in claim_tokens):
|
||||
reasons.append("continuation report missing issue claim status")
|
||||
if branch and branch.lower() not in lower:
|
||||
reasons.append("continuation report missing branch name")
|
||||
|
||||
@@ -1817,12 +1914,26 @@ def assess_issue_selection_final_report(
|
||||
report_text,
|
||||
pr_number=proof.get("pr_number"),
|
||||
pr_author=proof.get("pr_author"),
|
||||
issue_number=proof.get("issue_number"),
|
||||
issue_claim_status=proof.get("issue_claim_status"),
|
||||
branch=proof.get("branch"),
|
||||
old_head_sha=proof.get("old_head_sha"),
|
||||
new_head_sha=proof.get("new_head_sha"),
|
||||
session_authored_pr=proof.get("session_authored_pr"),
|
||||
continuation_allowed_reason=proof.get("continuation_allowed_reason"),
|
||||
)
|
||||
push_proof = proof.get("push_proof") or {}
|
||||
checks["force_with_lease_push"] = assess_force_with_lease_push_report(
|
||||
report_text,
|
||||
old_remote_head=push_proof.get("old_remote_head"),
|
||||
expected_lease_head=push_proof.get("expected_lease_head"),
|
||||
new_pushed_head=push_proof.get("new_pushed_head"),
|
||||
branch_pushed=push_proof.get("branch_pushed"),
|
||||
used_force_with_lease=push_proof.get("used_force_with_lease"),
|
||||
)
|
||||
sweep = proof.get("secret_sweep")
|
||||
if sweep is not None:
|
||||
checks["secret_sweep"] = assess_secret_sweep(sweep)
|
||||
|
||||
reasons = []
|
||||
downgraded = False
|
||||
@@ -1831,6 +1942,11 @@ def assess_issue_selection_final_report(
|
||||
if verdict in ("missing", "incomplete"):
|
||||
downgraded = True
|
||||
reasons.extend(result.get("reasons") or [])
|
||||
elif result.get("proven") is False:
|
||||
downgraded = True
|
||||
reasons.extend(
|
||||
f"{name}: {r}" for r in (result.get("reasons") or [])
|
||||
)
|
||||
elif result.get("downgraded") or not result.get("complete", True):
|
||||
downgraded = True
|
||||
reasons.extend(
|
||||
|
||||
Reference in New Issue
Block a user