feat(api): expose github account suspension preflight

This commit is contained in:
Your Name
2026-06-28 09:54:26 +08:00
parent f8e2b39ab3
commit e7542632ae
7 changed files with 359 additions and 212 deletions

View File

@@ -114,6 +114,13 @@ def build_delivery_closure_workbench(
"github_write_channel_ready"
)
is True,
"github_account_status": str(
github_preflight.get("github_account_status") or "unknown"
),
"github_account_suspended": github_preflight.get(
"github_account_suspended"
)
is True,
},
"href": "/governance?tab=automation-inventory",
"next_action": str(
@@ -231,6 +238,16 @@ def build_delivery_closure_workbench(
"github_write_channel_ready"
)
is True,
"github_account_status": str(
github_preflight.get("github_account_status") or "unknown"
),
"github_account_suspended": github_preflight.get(
"github_account_suspended"
)
is True,
"github_api_forbidden_count": _int(
github_preflight.get("github_api_forbidden_count")
),
"github_controlled_apply_ready_count": _int(
github_preflight.get("controlled_apply_ready_count")
),

View File

@@ -781,6 +781,13 @@ def build_github_target_private_backup_evidence_gate(
"github_write_channel_ready": controlled_preflight[
"github_write_channel_ready"
],
"github_account_status": controlled_preflight["github_account_status"],
"github_account_suspended": controlled_preflight[
"github_account_suspended"
],
"github_api_forbidden_count": controlled_preflight[
"github_api_forbidden_count"
],
"github_create_repo_channel_ready": controlled_preflight[
"github_create_repo_channel_ready"
],
@@ -1232,6 +1239,18 @@ def _build_target(
"canonical_source_ready": controlled_execution_preflight.get(
"canonical_source_ready"
),
"canonical_source_ref": controlled_execution_preflight.get(
"canonical_source_ref"
),
"canonical_source_sha": controlled_execution_preflight.get(
"canonical_source_sha"
),
"source_probe_status": controlled_execution_preflight.get(
"source_probe_status"
),
"source_warnings": _strings(
controlled_execution_preflight.get("source_warnings")
),
"github_collision_preflight_ready": controlled_execution_preflight.get(
"github_collision_preflight_ready"
),
@@ -1725,6 +1744,11 @@ def _require_controlled_execution_preflight_consistency(
github_404_count = sum(
1 for row in targets if row.get("github_readback_status") == "api_404_not_found"
)
github_forbidden_count = sum(
1
for row in targets
if row.get("github_readback_status") == "api_403_account_suspended"
)
source_ready_count = sum(
1 for row in targets if row.get("source_preflight_ready") is True
)
@@ -1742,6 +1766,8 @@ def _require_controlled_execution_preflight_consistency(
!= github_404_count
):
raise ValueError(f"{label}: GitHub 404 count must match targets")
if _int(summary.get("github_api_forbidden_count")) != github_forbidden_count + 1:
raise ValueError(f"{label}: GitHub forbidden count must match checked repos")
if _int(summary.get("source_preflight_ready_count")) != source_ready_count:
raise ValueError(f"{label}: source preflight ready count must match targets")
if _int(summary.get("create_private_repo_apply_ready_count")) != create_ready_count:
@@ -2103,6 +2129,9 @@ def _controlled_execution_preflight_readiness(
and authorization_summary["repo_creation_authorized"] is True
and authorization_summary["refs_sync_authorized"] is True,
"github_write_channel_ready": write_channel_ready,
"github_account_status": str(summary.get("github_account_status") or "unknown"),
"github_account_suspended": summary.get("github_account_suspended") is True,
"github_api_forbidden_count": _int(summary.get("github_api_forbidden_count")),
"github_create_repo_channel_ready": create_channel_ready,
"github_refs_sync_channel_ready": refs_channel_ready,
"github_connector_repo_creation_tool_available": summary.get(
@@ -2166,6 +2195,10 @@ def _controlled_execution_target_summary(value: Any) -> dict[str, Any]:
"target_selector": str(row.get("target_selector") or ""),
"source_resolution_status": str(row.get("source_resolution_status") or ""),
"source_candidate_type": str(row.get("source_candidate_type") or ""),
"canonical_source_ref": str(row.get("canonical_source_ref") or ""),
"canonical_source_sha": str(row.get("canonical_source_sha") or ""),
"source_probe_status": str(row.get("source_probe_status") or ""),
"source_warnings": _strings(row.get("source_warnings")),
"source_preflight_ready": row.get("source_preflight_ready") is True,
"canonical_source_ready": row.get("canonical_source_ready") is True,
"github_collision_preflight_ready": row.get("github_collision_preflight_ready")

View File

@@ -25,6 +25,9 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary():
assert data["summary"]["refs_sync_authorized"] is True
assert data["summary"]["workflow_trigger_authorized"] is True
assert data["summary"]["github_write_channel_ready"] is False
assert data["summary"]["github_account_status"] == "suspended"
assert data["summary"]["github_account_suspended"] is True
assert data["summary"]["github_api_forbidden_count"] == 6
assert data["summary"]["github_controlled_apply_ready_count"] == 0
assert data["summary"]["github_blocked_preflight_target_count"] == 5
assert data["summary"]["secret_values_collected"] is False
@@ -48,13 +51,15 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary():
assert lanes["github"]["blocker_count"] == 5
assert (
lanes["github"]["status"]
== "blocked_github_write_channel_and_source_preflight_required"
== "blocked_github_account_suspended_and_write_channel_required"
)
assert lanes["github"]["metric"]["verified"] == 4
assert lanes["github"]["metric"]["total"] == 9
assert lanes["github"]["metric"]["controlled_apply_ready"] == 0
assert lanes["github"]["metric"]["blocked_preflight"] == 5
assert lanes["github"]["metric"]["write_channel_ready"] is False
assert lanes["github"]["metric"]["github_account_status"] == "suspended"
assert lanes["github"]["metric"]["github_account_suspended"] is True
assert all(0 <= lane["completion_percent"] <= 100 for lane in lanes.values())
assert all(lane["tone"] in {"ok", "warn", "danger"} for lane in lanes.values())

View File

@@ -70,7 +70,10 @@ def test_load_github_target_private_backup_evidence_gate_from_committed_snapshot
snapshot["summary"]["github_missing_target_controlled_apply_ready_count"] == 0
)
assert snapshot["summary"]["github_missing_target_blocked_preflight_count"] == 5
assert snapshot["summary"]["github_missing_target_github_404_count"] == 5
assert snapshot["summary"]["github_missing_target_github_404_count"] == 0
assert snapshot["summary"]["github_account_status"] == "suspended"
assert snapshot["summary"]["github_account_suspended"] is True
assert snapshot["summary"]["github_api_forbidden_count"] == 6
assert snapshot["summary"]["private_backup_verified_count"] == 4
assert snapshot["summary"]["private_visibility_verified_count"] == 4
assert snapshot["summary"]["safe_credential_required_count"] == 9
@@ -228,14 +231,18 @@ def test_load_github_target_private_backup_evidence_gate_from_committed_snapshot
== "github_target_controlled_execution_preflight_v1"
)
assert controlled_preflight["status"] == (
"blocked_github_write_channel_and_source_preflight_required"
"blocked_github_account_suspended_and_write_channel_required"
)
assert controlled_preflight["authorization_ready"] is True
assert controlled_preflight["preflight_ready"] is False
assert controlled_preflight["github_write_channel_ready"] is False
assert controlled_preflight["github_account_status"] == "suspended"
assert controlled_preflight["github_account_suspended"] is True
assert controlled_preflight["github_api_forbidden_count"] == 6
assert controlled_preflight["github_create_repo_channel_ready"] is False
assert controlled_preflight["github_refs_sync_channel_ready"] is False
assert controlled_preflight["github_connector_missing_target_404_count"] == 5
assert controlled_preflight["source_preflight_ready_count"] == 5
assert controlled_preflight["github_connector_missing_target_404_count"] == 0
assert controlled_preflight["blocked_preflight_target_count"] == 5
assert controlled_preflight["controlled_apply_ready_count"] == 0
assert (
@@ -315,10 +322,14 @@ def test_load_github_target_private_backup_evidence_gate_from_committed_snapshot
assert targets["owenhytsai/ewoooc"]["refs_sync_authorized"] is True
assert targets["owenhytsai/ewoooc"]["execution_ready"] is True
assert targets["owenhytsai/ewoooc"]["controlled_apply_ready"] is False
assert targets["owenhytsai/ewoooc"]["source_preflight_ready"] is False
assert targets["owenhytsai/ewoooc"]["canonical_source_ready"] is False
assert targets["owenhytsai/ewoooc"]["source_preflight_ready"] is True
assert targets["owenhytsai/ewoooc"]["canonical_source_ready"] is True
assert (
"github_create_repo_channel_unavailable"
targets["owenhytsai/ewoooc"]["canonical_source_sha"]
== "f3e412cd211f5e4601204b256aeb95eae073b441"
)
assert (
"github_account_suspended_403"
in targets["owenhytsai/ewoooc"]["controlled_apply_blockers"]
)
assert targets["owenhytsai/ewoooc"]["private_backup_verified"] is False
@@ -362,17 +373,20 @@ def test_load_github_target_controlled_execution_preflight_from_committed_snapsh
)
assert (
preflight["status"]
== "blocked_github_write_channel_and_source_preflight_required"
== "blocked_github_account_suspended_and_write_channel_required"
)
assert preflight["authorization_ready"] is True
assert preflight["preflight_ready"] is False
assert preflight["github_write_channel_ready"] is False
assert preflight["github_account_status"] == "suspended"
assert preflight["github_account_suspended"] is True
assert preflight["github_api_forbidden_count"] == 6
assert preflight["github_create_repo_channel_ready"] is False
assert preflight["github_refs_sync_channel_ready"] is False
assert preflight["source_preflight_ready_count"] == 0
assert preflight["source_preflight_ready_count"] == 5
assert preflight["controlled_apply_ready_count"] == 0
assert preflight["blocked_preflight_target_count"] == 5
assert preflight["github_connector_missing_target_404_count"] == 5
assert preflight["github_connector_missing_target_404_count"] == 0
assert preflight["operation_boundaries"]["controlled_apply_allowed"] is False
assert preflight["operation_boundaries"]["secret_value_collection_allowed"] is False
assert "private_clone_url_credential" in preflight["still_forbidden"]
@@ -381,9 +395,13 @@ def test_load_github_target_controlled_execution_preflight_from_committed_snapsh
target_by_repo["owenhytsai/bitan-pharmacy"]["controlled_apply_ready"] is False
)
assert (
"local_worktree_has_tracked_changes"
"github_account_suspended_403"
in target_by_repo["owenhytsai/bitan-pharmacy"]["blockers"]
)
assert (
target_by_repo["owenhytsai/bitan-pharmacy"]["canonical_source_sha"]
== "e122c8cbd9522999fd9844c2b63790fadcc89c20"
)
assert target_by_repo["owenhytsai/VibeWork"]["refs_sync_apply_ready"] is False

View File

@@ -37,7 +37,10 @@ def test_github_target_private_backup_evidence_gate_endpoint_returns_read_only_g
assert data["summary"]["github_refs_sync_channel_ready"] is False
assert data["summary"]["github_missing_target_controlled_apply_ready_count"] == 0
assert data["summary"]["github_missing_target_blocked_preflight_count"] == 5
assert data["summary"]["github_missing_target_github_404_count"] == 5
assert data["summary"]["github_missing_target_github_404_count"] == 0
assert data["summary"]["github_account_status"] == "suspended"
assert data["summary"]["github_account_suspended"] is True
assert data["summary"]["github_api_forbidden_count"] == 6
assert data["summary"]["private_backup_verified_count"] == 4
assert data["summary"]["private_visibility_verified_count"] == 4
assert data["summary"]["safe_credential_evidence_intake_ready"] is True
@@ -141,22 +144,26 @@ def test_github_target_controlled_execution_preflight_endpoint_returns_write_gap
data = response.json()
assert data["schema_version"] == "github_target_controlled_execution_preflight_v1"
assert (
data["status"] == "blocked_github_write_channel_and_source_preflight_required"
data["status"]
== "blocked_github_account_suspended_and_write_channel_required"
)
assert data["authorization_ready"] is True
assert data["preflight_ready"] is False
assert data["github_write_channel_ready"] is False
assert data["github_account_status"] == "suspended"
assert data["github_account_suspended"] is True
assert data["github_api_forbidden_count"] == 6
assert data["github_create_repo_channel_ready"] is False
assert data["github_refs_sync_channel_ready"] is False
assert data["source_preflight_ready_count"] == 0
assert data["source_preflight_ready_count"] == 5
assert data["controlled_apply_ready_count"] == 0
assert data["blocked_preflight_target_count"] == 5
assert data["github_connector_missing_target_404_count"] == 5
assert data["github_connector_missing_target_404_count"] == 0
assert data["operation_boundaries"]["controlled_apply_allowed"] is False
assert data["operation_boundaries"]["secret_value_collection_allowed"] is False
assert data["tool_channel_readback"]["gh_cli_write_ready"] is False
assert data["targets"][0]["controlled_apply_ready"] is False
assert "github_create_repo_channel_unavailable" in data["targets"][0]["blockers"]
assert "github_account_suspended_403" in data["targets"][0]["blockers"]
assert "192.168.0." not in response.text