fix(github): expose operator unblock actions
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user