fix(github): expose operator unblock actions

This commit is contained in:
Your Name
2026-06-28 14:55:18 +08:00
parent e7c5013963
commit 96dfb53550
6 changed files with 184 additions and 24 deletions

View File

@@ -54,6 +54,7 @@ def build_delivery_closure_workbench(
github_summary = _dict(github.get("summary"))
github_boundaries = _dict(github.get("operation_boundaries"))
github_preflight = _dict(github.get("controlled_execution_preflight"))
github_operator_unblock = _dict(github_preflight.get("operator_unblock"))
gitea_status = _dict(gitea.get("program_status"))
gitea_rollups = _dict(gitea.get("rollups"))
runtime_status = _dict(runtime.get("program_status"))
@@ -123,7 +124,14 @@ def build_delivery_closure_workbench(
is True,
},
"href": "/governance?tab=automation-inventory",
"operator_unblock": github_operator_unblock,
"next_action": str(
_first_string(github_operator_unblock.get("required_actions"))
or github_operator_unblock.get("safe_handoff")
or github_preflight.get("operator_unblock_status")
or ""
)
or str(
_first_target_action(github_preflight.get("targets"))
or github.get("next_action")
or _first_target_action(github.get("targets"))
@@ -241,9 +249,7 @@ def build_delivery_closure_workbench(
"github_account_status": str(
github_preflight.get("github_account_status") or "unknown"
),
"github_account_suspended": github_preflight.get(
"github_account_suspended"
)
"github_account_suspended": github_preflight.get("github_account_suspended")
is True,
"github_api_forbidden_count": _int(
github_preflight.get("github_api_forbidden_count")
@@ -252,6 +258,11 @@ def build_delivery_closure_workbench(
github_preflight.get("controlled_apply_ready_count")
),
"github_blocked_preflight_target_count": github_preflight_blockers,
"github_operator_unblock_required": github_operator_unblock.get("required")
is True,
"github_operator_unblock_status": str(
github_operator_unblock.get("status") or ""
),
"secret_values_collected": False,
},
"source_statuses": source_statuses,

View File

@@ -35,6 +35,20 @@ _EXECUTION_AUTHORIZATION_SCHEMA_VERSION = (
_CONTROLLED_EXECUTION_PREFLIGHT_SCHEMA_VERSION = (
"github_target_controlled_execution_preflight_v1"
)
_GITHUB_WRITE_CHANNEL_RECHECK_COMMANDS = [
"gh api /user --jq '{login:.login}'",
"git push --dry-run origin HEAD:refs/heads/codex-github-write-channel-readonly-check",
]
_GITHUB_OPERATOR_UNBLOCK_ACTIONS = [
"complete_github_account_suspension_appeal_or_provide_authorized_writable_namespace",
"refresh_local_github_cli_login_without_sharing_tokens_or_cookies",
"rerun_github_write_channel_dry_run_before_create_or_refs_sync",
]
_GITHUB_OPERATOR_STILL_FORBIDDEN = [
"do_not_paste_pat_token_private_key_cookie_session_or_authorization_header",
"do_not_collect_private_clone_url_or_credential_value",
"do_not_force_push_delete_refs_or_change_public_visibility",
]
_PREFLIGHT_SCHEMA_VERSION = "github_target_owner_response_intake_preflight_v1"
_PREFLIGHT_MODE = "validate_owner_response_only_no_persist_no_github_write"
_SAFE_CREDENTIAL_REVIEW_SCHEMA_VERSION = (
@@ -2092,6 +2106,9 @@ def _controlled_execution_preflight_readiness(
create_channel_ready = summary.get("github_create_repo_channel_ready") is True
refs_channel_ready = summary.get("github_refs_sync_channel_ready") is True
write_channel_ready = create_channel_ready and refs_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"))
preflight_ready = (
bool(preflight_targets)
and authorization_summary["authorization_present"] is True
@@ -2110,6 +2127,12 @@ def _controlled_execution_preflight_readiness(
for row in preflight_targets
if row.get("github_repo")
}
operator_unblock = _github_write_channel_operator_unblock(
github_account_status=github_account_status,
github_account_suspended=github_account_suspended,
github_api_forbidden_count=github_api_forbidden_count,
github_write_channel_ready=write_channel_ready,
)
return {
"schema_version": str(
payload.get("schema_version")
@@ -2129,9 +2152,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_account_status": github_account_status,
"github_account_suspended": github_account_suspended,
"github_api_forbidden_count": 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(
@@ -2161,6 +2184,9 @@ def _controlled_execution_preflight_readiness(
"required_preflight_checks": _strings(payload.get("required_preflight_checks")),
"rollback_plan": _dict(payload.get("rollback_plan")),
"post_apply_verifiers": _strings(payload.get("post_apply_verifiers")),
"operator_unblock_required": operator_unblock["required"],
"operator_unblock_status": operator_unblock["status"],
"operator_unblock": operator_unblock,
"operation_boundaries": {
"read_only_api_allowed": boundaries.get("read_only_api_allowed") is True,
"github_api_write_allowed_by_authorization": boundaries.get(
@@ -2187,6 +2213,40 @@ def _controlled_execution_preflight_readiness(
}
def _github_write_channel_operator_unblock(
*,
github_account_status: str,
github_account_suspended: bool,
github_api_forbidden_count: int,
github_write_channel_ready: bool,
) -> dict[str, Any]:
required = not github_write_channel_ready
if github_account_suspended:
status = "github_account_suspended_external_action_required"
elif required:
status = "github_write_channel_reauthentication_or_namespace_required"
else:
status = "github_write_channel_ready_no_operator_action"
return {
"required": required,
"status": status,
"github_account_status": github_account_status,
"github_account_suspended": github_account_suspended,
"github_api_forbidden_count": github_api_forbidden_count,
"required_actions": _GITHUB_OPERATOR_UNBLOCK_ACTIONS if required else [],
"recheck_commands": _GITHUB_WRITE_CHANNEL_RECHECK_COMMANDS if required else [],
"still_forbidden": _GITHUB_OPERATOR_STILL_FORBIDDEN,
"safe_handoff": (
"GitHub owner must restore the suspended account or provide a writable "
"private GitHub namespace. Do not share tokens, cookies, private keys, "
"authorization headers, or private clone URLs."
)
if required
else "GitHub write channel is ready for controlled apply preflight.",
}
def _controlled_execution_target_summary(value: Any) -> dict[str, Any]:
row = _dict(value)
return {

View File

@@ -30,6 +30,10 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary():
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"]["github_operator_unblock_required"] is True
assert data["summary"]["github_operator_unblock_status"] == (
"github_account_suspended_external_action_required"
)
assert data["summary"]["secret_values_collected"] is False
assert data["summary"]["average_completion_percent"] >= 0
assert data["summary"]["high_risk_blocker_count"] > 0
@@ -64,6 +68,17 @@ def test_delivery_closure_workbench_endpoint_returns_product_summary():
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 lanes["github"]["operator_unblock"]["required"] is True
assert lanes["github"]["operator_unblock"]["status"] == (
"github_account_suspended_external_action_required"
)
assert (
"complete_github_account_suspension_appeal_or_provide_authorized_writable_namespace"
in lanes["github"]["operator_unblock"]["required_actions"]
)
assert lanes["github"]["next_action"] == (
"complete_github_account_suspension_appeal_or_provide_authorized_writable_namespace"
)
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

@@ -245,6 +245,25 @@ def test_load_github_target_private_backup_evidence_gate_from_committed_snapshot
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 controlled_preflight["operator_unblock_required"] is True
assert (
controlled_preflight["operator_unblock_status"]
== "github_account_suspended_external_action_required"
)
operator_unblock = controlled_preflight["operator_unblock"]
assert operator_unblock["required"] is True
assert operator_unblock["github_account_status"] == "suspended"
assert operator_unblock["github_account_suspended"] is True
assert operator_unblock["github_api_forbidden_count"] == 6
assert (
"complete_github_account_suspension_appeal_or_provide_authorized_writable_namespace"
in operator_unblock["required_actions"]
)
assert "gh api /user --jq '{login:.login}'" in operator_unblock["recheck_commands"]
assert (
"do_not_paste_pat_token_private_key_cookie_session_or_authorization_header"
in operator_unblock["still_forbidden"]
)
assert (
controlled_preflight["operation_boundaries"]["controlled_apply_allowed"]
is False

View File

@@ -129,6 +129,19 @@ def test_github_target_private_backup_evidence_gate_endpoint_returns_read_only_g
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["operator_unblock_required"] is True
assert (
controlled_preflight["operator_unblock_status"]
== "github_account_suspended_external_action_required"
)
assert (
"complete_github_account_suspension_appeal_or_provide_authorized_writable_namespace"
in controlled_preflight["operator_unblock"]["required_actions"]
)
assert (
"git push --dry-run origin HEAD:refs/heads/codex-github-write-channel-readonly-check"
in controlled_preflight["operator_unblock"]["recheck_commands"]
)
assert controlled_preflight["blocked_preflight_target_count"] == 5
assert "192.168.0." not in response.text
@@ -144,8 +157,7 @@ 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_account_suspended_and_write_channel_required"
data["status"] == "blocked_github_account_suspended_and_write_channel_required"
)
assert data["authorization_ready"] is True
assert data["preflight_ready"] is False
@@ -153,6 +165,15 @@ def test_github_target_controlled_execution_preflight_endpoint_returns_write_gap
assert data["github_account_status"] == "suspended"
assert data["github_account_suspended"] is True
assert data["github_api_forbidden_count"] == 6
assert data["operator_unblock_required"] is True
assert data["operator_unblock_status"] == (
"github_account_suspended_external_action_required"
)
assert data["operator_unblock"]["github_account_suspended"] is True
assert (
"do_not_paste_pat_token_private_key_cookie_session_or_redacted_authorization_header"
in data["operator_unblock"]["still_forbidden"]
)
assert data["github_create_repo_channel_ready"] is False
assert data["github_refs_sync_channel_ready"] is False
assert data["source_preflight_ready_count"] == 5

View File

@@ -2429,6 +2429,18 @@ export interface AwoooIStatusCleanupDashboardSnapshot {
ui_implementation_authorized: false
}
export interface DeliveryOperatorUnblock {
required: boolean
status: string
github_account_status: string
github_account_suspended: boolean
github_api_forbidden_count: number
required_actions: string[]
recheck_commands: string[]
still_forbidden: string[]
safe_handoff: string
}
export interface DeliveryClosureWorkbenchSnapshot {
schema_version: 'delivery_closure_workbench_v1'
generated_at: string
@@ -2438,18 +2450,28 @@ export interface DeliveryClosureWorkbenchSnapshot {
loaded_source_count: number
average_completion_percent: number
high_risk_blocker_count: number
runtime_execution_authorized: false
remote_write_authorized: false
repo_creation_authorized: false
refs_sync_authorized: false
workflow_trigger_authorized: false
secret_values_collected: false
runtime_execution_authorized: boolean
remote_write_authorized: boolean
repo_creation_authorized: boolean
visibility_change_authorized: boolean
refs_sync_authorized: boolean
workflow_trigger_authorized: boolean
github_write_channel_ready: boolean
github_account_status: string
github_account_suspended: boolean
github_api_forbidden_count: number
github_controlled_apply_ready_count: number
github_blocked_preflight_target_count: number
github_operator_unblock_required: boolean
github_operator_unblock_status: string
secret_values_collected: boolean
}
source_statuses: Array<{
id: string
loaded: boolean
schema_version: string
generated_at: string
missing_reason: string
}>
lanes: Array<{
id: 'release' | 'github' | 'gitea' | 'runtime' | 'backup'
@@ -2459,11 +2481,21 @@ export interface DeliveryClosureWorkbenchSnapshot {
blocker_count: number
metric:
| { kind: 'blocked_gate'; blocked: number; total: number }
| { kind: 'private_backup_verified'; verified: number; total: number }
| {
kind: 'private_backup_verified'
verified: number
total: number
controlled_apply_ready: number
blocked_preflight: number
write_channel_ready: boolean
github_account_status: string
github_account_suspended: boolean
}
| { kind: 'workflow_count'; count: number }
| { kind: 'surface_count'; total: number }
| { kind: 'readiness_row_count'; rows: number }
href: string
operator_unblock?: DeliveryOperatorUnblock
next_action: string
tone: 'ok' | 'warn' | 'danger'
}>
@@ -2475,15 +2507,17 @@ export interface DeliveryClosureWorkbenchSnapshot {
}>
operation_boundaries: {
read_only_api_allowed: true
runtime_write_allowed: false
remote_write_allowed: false
repo_creation_allowed: false
visibility_change_allowed: false
refs_sync_allowed: false
workflow_trigger_allowed: false
secret_value_collection_allowed: false
backup_restore_execution_allowed: false
active_scan_allowed: false
runtime_write_allowed: boolean
remote_write_allowed: boolean
repo_creation_allowed: boolean
visibility_change_allowed: boolean
refs_sync_allowed: boolean
workflow_trigger_allowed: boolean
github_write_channel_ready: boolean
github_controlled_apply_allowed: boolean
secret_value_collection_allowed: boolean
backup_restore_execution_allowed: boolean
active_scan_allowed: boolean
}
}