feat(api): expose github account suspension preflight
This commit is contained in:
@@ -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")
|
||||
),
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -48226,3 +48226,34 @@ production browser smoke:
|
||||
**下一個 P0**:
|
||||
- commit feature,正常 push 到 Gitea;若 main CD idle/success,normal push `HEAD:main`,部署後 production readback 目標:`github_write_channel_ready=false`、`github_missing_target_controlled_apply_ready_count=0`、`blocked_preflight_target_count=5`,並確認 Workbench GitHub lane 顯示 preflight blocker。
|
||||
- 後續真正 controlled apply 需要補 GitHub create repo channel 或可用 refs sync channel,並逐 target 產生 source-of-truth diff / no-force dry-run;仍不讀 secret、不收 private clone URL、不 force push。
|
||||
|
||||
## 2026-06-28 — 09:50 GitHub account suspended readback 與 source selector preflight 本地完成
|
||||
|
||||
**時間與來源**:
|
||||
- 2026-06-28 09:30-09:50 Asia/Taipei。
|
||||
- 來源:GitHub connector `_get_repo`、`git ls-remote origin`、本地 / 內網 git remote readback、feature branch `codex/github-redacted-evidence-validator-20260627`。
|
||||
|
||||
**完成內容**:
|
||||
- GitHub live readback 從早前 404 / private admin 狀態變成 account-level 403:connector 對 `owenhytsai/awoooi` 與 missing target 均回 `Sorry. Your account was suspended`;`git remote show origin` / `git ls-remote origin` 也回 `Your account is suspended`。
|
||||
- `docs/security/github-target-controlled-execution-preflight.snapshot.json` 更新為 `status=blocked_github_account_suspended_and_write_channel_required`。
|
||||
- controlled preflight 新增並固定:`github_account_status=suspended`、`github_account_suspended=true`、`github_api_forbidden_count=6`、`github_connector_missing_target_404_count=0`。
|
||||
- 5 個 missing target 的 source selector 已補齊到 committed remote main,不把未提交本地變更偷偷納入 GitHub backup:
|
||||
- `owenhytsai/ewoooc`:`gitea:wooo/ewoooc:main` → `f3e412cd211f5e4601204b256aeb95eae073b441`。
|
||||
- `owenhytsai/bitan-pharmacy`:`internal-origin:bitan-pharmacy:main` → `e122c8cbd9522999fd9844c2b63790fadcc89c20`;本地 64 個 tracked changes 與 untracked changes 另列 source warning。
|
||||
- `owenhytsai/tsenyang-website`:`internal-origin:tsenyang-website:main` → `b369ed8cb6666e8ddaa2d31e1418a02794fdea9d`;本地 29 個 tracked changes 與 untracked outputs 另列 source warning。
|
||||
- `owenhytsai/VibeWork`:`gitea:wooo/vibework:main` → `76a4ee15026af278a3660ad4b4547e9308b107be`;本地 `48275cc52be79107e887147d3fe10310a887afe9` divergence 另列 source warning。
|
||||
- `owenhytsai/agent-bounty-protocol`:`gitea:wooo/agent-bounty-protocol:main` → `b7a733f44f4f645dd21a9b4a9075b89c4a324f64`;本地 `0601df8bd9c0aaedb9ce3a226a6f1aeca645ca0a` divergence 另列 source warning,且不代表 runtime apply。
|
||||
- API / Workbench 新增 account suspended 欄位,GitHub lane 顯示 account 403,而不是誤導成單純缺 owner approval。
|
||||
|
||||
**本地驗證結果**:
|
||||
- `python3 -m json.tool docs/security/github-target-controlled-execution-preflight.snapshot.json`:通過。
|
||||
- `python3 -m py_compile apps/api/src/services/github_target_private_backup_evidence_gate.py apps/api/src/services/delivery_closure_workbench.py apps/api/src/api/v1/agents.py`:通過。
|
||||
- `python3.11 -m ruff check apps/api/src/services/github_target_private_backup_evidence_gate.py apps/api/src/services/delivery_closure_workbench.py apps/api/src/api/v1/agents.py apps/api/tests/test_github_target_private_backup_evidence_gate.py apps/api/tests/test_github_target_private_backup_evidence_gate_api.py apps/api/tests/test_delivery_closure_workbench_api.py`:通過。
|
||||
- `DATABASE_URL=sqlite:///test.db PYTHONPATH=apps/api python3.11 -m pytest apps/api/tests/test_github_target_private_backup_evidence_gate.py apps/api/tests/test_github_target_private_backup_evidence_gate_api.py apps/api/tests/test_delivery_closure_workbench_api.py -q`:`20 passed`。
|
||||
- `git diff --check`:通過。
|
||||
- 本地 readback snippet:gate `github_account_status=suspended`、`github_account_suspended=True`、`github_api_forbidden_count=6`;preflight `source_preflight_ready_count=5`、`controlled_apply_ready_count=0`、`github_connector_missing_target_404_count=0`;Delivery Workbench GitHub lane `status=blocked_github_account_suspended_and_write_channel_required`、`blocker_count=5`。
|
||||
|
||||
**為什麼仍不能全部推上 GitHub**:
|
||||
- 不是 owner / read-only / manual gate;受控授權已開,source selector 也已補到 5/5。
|
||||
- 目前唯一立即阻擋是 GitHub account-level suspension / 403,導致 create repo、private visibility readback、refs sync 都沒有可用通道。
|
||||
- 不讀 token、不收 private clone URL、不用 force push、不建立 public repo、不刪 refs;GitHub 帳號恢復或出現不讀 secret 的 create/write channel 後,下一步就是依 5 個 canonical source SHA 建 private repo 並 normal push。
|
||||
|
||||
@@ -1,202 +1,238 @@
|
||||
{
|
||||
"schema_version": "github_target_controlled_execution_preflight_v1",
|
||||
"generated_at": "2026-06-28T08:44:54+08:00",
|
||||
"status": "blocked_github_write_channel_and_source_preflight_required",
|
||||
"mode": "controlled_apply_preflight_no_secret_no_repo_write",
|
||||
"authorization_source": "chat_authorization_2026-06-28_full_hard_gate_open",
|
||||
"summary": {
|
||||
"owner_execution_authorization_received_count": 1,
|
||||
"authorized_missing_target_count": 5,
|
||||
"github_connector_get_repo_checked_count": 6,
|
||||
"github_connector_existing_private_admin_count": 1,
|
||||
"github_connector_missing_target_404_count": 5,
|
||||
"local_gh_auth_ready": false,
|
||||
"github_connector_repo_creation_tool_available": false,
|
||||
"github_connector_refs_write_tool_available": true,
|
||||
"github_create_repo_channel_ready": false,
|
||||
"github_refs_sync_channel_ready": false,
|
||||
"source_preflight_ready_count": 0,
|
||||
"create_private_repo_apply_ready_count": 0,
|
||||
"refs_sync_apply_ready_count": 0,
|
||||
"blocked_preflight_target_count": 5,
|
||||
"write_performed": false,
|
||||
"repo_creation_performed": false,
|
||||
"visibility_change_performed": false,
|
||||
"refs_sync_performed": false,
|
||||
"workflow_trigger_performed": false,
|
||||
"secret_values_collected": false,
|
||||
"private_clone_urls_collected": false
|
||||
},
|
||||
"tool_channel_readback": {
|
||||
"gh_cli_auth_status": "invalid_token_in_keyring",
|
||||
"gh_cli_write_ready": false,
|
||||
"github_connector_read_repo_ready": true,
|
||||
"github_connector_create_repo_ready": false,
|
||||
"github_connector_refs_write_ready": true,
|
||||
"github_connector_verified_private_admin_repo": "owenhytsai/awoooi",
|
||||
"github_connector_missing_target_status": "api_404_not_found",
|
||||
"credential_collection_attempted": false,
|
||||
"secret_value_collection_allowed": false
|
||||
},
|
||||
"required_preflight_checks": [
|
||||
"confirm_target_owner_scope_is_owenhytsai",
|
||||
"verify_github_repo_collision_with_authenticated_readback",
|
||||
"select_canonical_source_without_copying_secret_values",
|
||||
"verify_source_worktree_clean_or_pick_remote_source",
|
||||
"build_source_of_truth_ref_diff",
|
||||
"run_no_force_refs_sync_dry_run",
|
||||
"confirm_private_visibility_post_create",
|
||||
"run_post_sync_refs_readback"
|
||||
],
|
||||
"rollback_plan": {
|
||||
"repo_creation": "new_private_repo_can_be_left_empty_or_archived_after_no_ref_sync; repo_delete_is_not_authorized_here",
|
||||
"refs_sync": "normal_push_only; rollback_requires new forward commit or branch restore, never force push or ref delete",
|
||||
"visibility": "private_only; public_visibility_is_forbidden",
|
||||
"workflow_trigger": "post_sync_verification_only_after_refs_readback"
|
||||
},
|
||||
"post_apply_verifiers": [
|
||||
"github_get_repo_visibility_private",
|
||||
"github_default_branch_readback",
|
||||
"github_refs_compare_against_canonical_source",
|
||||
"awoooi_github_private_backup_evidence_gate_readback",
|
||||
"delivery_closure_workbench_readback"
|
||||
],
|
||||
"operation_boundaries": {
|
||||
"read_only_api_allowed": true,
|
||||
"github_api_write_allowed_by_authorization": true,
|
||||
"github_create_repo_channel_ready": false,
|
||||
"github_refs_sync_channel_ready": false,
|
||||
"controlled_apply_allowed": false,
|
||||
"repo_creation_allowed": false,
|
||||
"visibility_change_allowed": false,
|
||||
"refs_sync_allowed": false,
|
||||
"workflow_trigger_allowed": false,
|
||||
"force_push_allowed": false,
|
||||
"delete_refs_allowed": false,
|
||||
"public_visibility_allowed": false,
|
||||
"github_primary_switch_allowed": false,
|
||||
"secret_value_collection_allowed": false,
|
||||
"private_clone_url_collection_allowed": false,
|
||||
"raw_payload_storage_allowed": false
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"github_repo": "owenhytsai/ewoooc",
|
||||
"github_readback_status": "api_404_not_found",
|
||||
"target_selector": "github_owner=owenhytsai repo=ewoooc source_candidate=wooo/ewoooc",
|
||||
"source_resolution_status": "blocked_canonical_source_ambiguous",
|
||||
"source_candidate_type": "gitea_repo_exists_with_momo_lineage_conflict",
|
||||
"source_preflight_ready": false,
|
||||
"canonical_source_ready": false,
|
||||
"github_collision_preflight_ready": true,
|
||||
"create_private_repo_apply_ready": false,
|
||||
"refs_sync_apply_ready": false,
|
||||
"controlled_apply_ready": false,
|
||||
"blockers": [
|
||||
"canonical_source_ambiguous",
|
||||
"momo_lineage_conflict_requires_source_truth_diff",
|
||||
"github_create_repo_channel_unavailable",
|
||||
"github_refs_sync_channel_unavailable"
|
||||
],
|
||||
"next_action": "produce ewoooc versus momo source-of-truth diff, then rerun create/sync dry-run"
|
||||
"schema_version": "github_target_controlled_execution_preflight_v1",
|
||||
"generated_at": "2026-06-28T09:48:36+08:00",
|
||||
"status": "blocked_github_account_suspended_and_write_channel_required",
|
||||
"mode": "controlled_apply_preflight_no_secret_no_repo_write",
|
||||
"authorization_source": "chat_authorization_2026-06-28_full_hard_gate_open",
|
||||
"summary": {
|
||||
"owner_execution_authorization_received_count": 1,
|
||||
"authorized_missing_target_count": 5,
|
||||
"github_connector_get_repo_checked_count": 6,
|
||||
"github_connector_readback_status": "api_403_account_suspended",
|
||||
"github_account_status": "suspended",
|
||||
"github_account_suspended": true,
|
||||
"github_api_forbidden_count": 6,
|
||||
"github_connector_existing_private_admin_count": 0,
|
||||
"github_connector_missing_target_404_count": 0,
|
||||
"local_gh_auth_ready": false,
|
||||
"github_connector_repo_creation_tool_available": false,
|
||||
"github_connector_refs_write_tool_available": true,
|
||||
"github_create_repo_channel_ready": false,
|
||||
"github_refs_sync_channel_ready": false,
|
||||
"source_preflight_ready_count": 5,
|
||||
"create_private_repo_apply_ready_count": 0,
|
||||
"refs_sync_apply_ready_count": 0,
|
||||
"blocked_preflight_target_count": 5,
|
||||
"write_performed": false,
|
||||
"repo_creation_performed": false,
|
||||
"visibility_change_performed": false,
|
||||
"refs_sync_performed": false,
|
||||
"workflow_trigger_performed": false,
|
||||
"secret_values_collected": false,
|
||||
"private_clone_urls_collected": false
|
||||
},
|
||||
{
|
||||
"github_repo": "owenhytsai/bitan-pharmacy",
|
||||
"github_readback_status": "api_404_not_found",
|
||||
"target_selector": "github_owner=owenhytsai repo=bitan-pharmacy source_candidate=local_internal_remote_snapshot",
|
||||
"source_resolution_status": "blocked_local_worktree_dirty",
|
||||
"source_candidate_type": "internal_remote_snapshot_candidate",
|
||||
"source_preflight_ready": false,
|
||||
"canonical_source_ready": false,
|
||||
"github_collision_preflight_ready": true,
|
||||
"create_private_repo_apply_ready": false,
|
||||
"refs_sync_apply_ready": false,
|
||||
"controlled_apply_ready": false,
|
||||
"blockers": [
|
||||
"local_worktree_has_tracked_changes",
|
||||
"canonical_remote_source_not_committed_as_gitea_repo",
|
||||
"github_create_repo_channel_unavailable",
|
||||
"github_refs_sync_channel_unavailable"
|
||||
],
|
||||
"next_action": "choose clean local or internal remote source, run source diff, then rerun no-force refs dry-run"
|
||||
"tool_channel_readback": {
|
||||
"gh_cli_auth_status": "invalid_token_in_keyring_account_suspended",
|
||||
"gh_cli_write_ready": false,
|
||||
"github_connector_read_repo_ready": false,
|
||||
"github_connector_create_repo_ready": false,
|
||||
"github_connector_refs_write_ready": false,
|
||||
"github_connector_verified_private_admin_repo": null,
|
||||
"github_connector_account_status": "suspended",
|
||||
"github_connector_forbidden_status": "api_403_account_suspended",
|
||||
"github_connector_missing_target_status": "api_403_account_suspended",
|
||||
"credential_collection_attempted": false,
|
||||
"secret_value_collection_allowed": false
|
||||
},
|
||||
{
|
||||
"github_repo": "owenhytsai/tsenyang-website",
|
||||
"github_readback_status": "api_404_not_found",
|
||||
"target_selector": "github_owner=owenhytsai repo=tsenyang-website source_candidate=local_internal_remote_snapshot",
|
||||
"source_resolution_status": "blocked_local_worktree_dirty",
|
||||
"source_candidate_type": "internal_remote_snapshot_candidate",
|
||||
"source_preflight_ready": false,
|
||||
"canonical_source_ready": false,
|
||||
"github_collision_preflight_ready": true,
|
||||
"create_private_repo_apply_ready": false,
|
||||
"refs_sync_apply_ready": false,
|
||||
"controlled_apply_ready": false,
|
||||
"blockers": [
|
||||
"local_worktree_has_tracked_and_untracked_changes",
|
||||
"canonical_remote_source_not_committed_as_gitea_repo",
|
||||
"github_create_repo_channel_unavailable",
|
||||
"github_refs_sync_channel_unavailable"
|
||||
],
|
||||
"next_action": "select clean canonical source for tsenyang-website, then rerun no-force refs dry-run"
|
||||
"required_preflight_checks": [
|
||||
"confirm_target_owner_scope_is_owenhytsai",
|
||||
"verify_github_repo_collision_with_authenticated_readback",
|
||||
"select_canonical_source_without_copying_secret_values",
|
||||
"verify_source_worktree_clean_or_pick_remote_source",
|
||||
"build_source_of_truth_ref_diff",
|
||||
"run_no_force_refs_sync_dry_run",
|
||||
"confirm_private_visibility_post_create",
|
||||
"run_post_sync_refs_readback"
|
||||
],
|
||||
"rollback_plan": {
|
||||
"repo_creation": "new_private_repo_can_be_left_empty_or_archived_after_no_ref_sync; repo_delete_is_not_authorized_here",
|
||||
"refs_sync": "normal_push_only; rollback_requires new forward commit or branch restore, never force push or ref delete",
|
||||
"visibility": "private_only; public_visibility_is_forbidden",
|
||||
"workflow_trigger": "post_sync_verification_only_after_refs_readback"
|
||||
},
|
||||
{
|
||||
"github_repo": "owenhytsai/VibeWork",
|
||||
"github_readback_status": "api_404_not_found",
|
||||
"target_selector": "github_owner=owenhytsai repo=VibeWork source_candidate=wooo/vibework",
|
||||
"source_resolution_status": "blocked_product_boundary_and_local_divergence",
|
||||
"source_candidate_type": "gitea_repo_exists_local_worktree_diverged",
|
||||
"source_preflight_ready": false,
|
||||
"canonical_source_ready": false,
|
||||
"github_collision_preflight_ready": true,
|
||||
"create_private_repo_apply_ready": false,
|
||||
"refs_sync_apply_ready": false,
|
||||
"controlled_apply_ready": false,
|
||||
"blockers": [
|
||||
"product_boundary_requires_source_selector",
|
||||
"local_worktree_ahead_behind_with_changes",
|
||||
"github_create_repo_channel_unavailable",
|
||||
"github_refs_sync_channel_unavailable"
|
||||
],
|
||||
"next_action": "select Gitea vibework or local VibeWork as canonical source, then run refs diff dry-run"
|
||||
"post_apply_verifiers": [
|
||||
"github_get_repo_visibility_private",
|
||||
"github_default_branch_readback",
|
||||
"github_refs_compare_against_canonical_source",
|
||||
"awoooi_github_private_backup_evidence_gate_readback",
|
||||
"delivery_closure_workbench_readback"
|
||||
],
|
||||
"operation_boundaries": {
|
||||
"read_only_api_allowed": true,
|
||||
"github_api_write_allowed_by_authorization": true,
|
||||
"github_create_repo_channel_ready": false,
|
||||
"github_refs_sync_channel_ready": false,
|
||||
"controlled_apply_allowed": false,
|
||||
"repo_creation_allowed": false,
|
||||
"visibility_change_allowed": false,
|
||||
"refs_sync_allowed": false,
|
||||
"workflow_trigger_allowed": false,
|
||||
"force_push_allowed": false,
|
||||
"delete_refs_allowed": false,
|
||||
"public_visibility_allowed": false,
|
||||
"github_primary_switch_allowed": false,
|
||||
"secret_value_collection_allowed": false,
|
||||
"private_clone_url_collection_allowed": false,
|
||||
"raw_payload_storage_allowed": false
|
||||
},
|
||||
{
|
||||
"github_repo": "owenhytsai/agent-bounty-protocol",
|
||||
"github_readback_status": "api_404_not_found",
|
||||
"target_selector": "github_owner=owenhytsai repo=agent-bounty-protocol source_candidate=wooo/agent-bounty-protocol",
|
||||
"source_resolution_status": "blocked_high_risk_runtime_surface",
|
||||
"source_candidate_type": "gitea_repo_exists_high_risk_runtime_surface",
|
||||
"source_preflight_ready": false,
|
||||
"canonical_source_ready": false,
|
||||
"github_collision_preflight_ready": true,
|
||||
"create_private_repo_apply_ready": false,
|
||||
"refs_sync_apply_ready": false,
|
||||
"controlled_apply_ready": false,
|
||||
"blockers": [
|
||||
"large_dirty_scan_not_completed",
|
||||
"runtime_surface_source_selector_required",
|
||||
"github_create_repo_channel_unavailable",
|
||||
"github_refs_sync_channel_unavailable"
|
||||
],
|
||||
"next_action": "finish bounded dirty/source scan without secrets, then run canonical refs dry-run"
|
||||
}
|
||||
],
|
||||
"still_forbidden": [
|
||||
"secret_value",
|
||||
"token_value",
|
||||
"private_key",
|
||||
"cookie_or_session",
|
||||
"authorization_header",
|
||||
"private_clone_url_credential",
|
||||
"repo_archive",
|
||||
"git_object_pack",
|
||||
"force_push",
|
||||
"delete_refs",
|
||||
"tag_rewrite",
|
||||
"repo_delete",
|
||||
"github_primary_switch",
|
||||
"public_visibility",
|
||||
"raw_runtime_secret_volume",
|
||||
"unrelated_history_merge"
|
||||
]
|
||||
"targets": [
|
||||
{
|
||||
"github_repo": "owenhytsai/ewoooc",
|
||||
"github_readback_status": "api_403_account_suspended",
|
||||
"target_selector": "github_owner=owenhytsai repo=ewoooc source_candidate=wooo/ewoooc",
|
||||
"source_resolution_status": "ready_gitea_main_selected_stale_momo_worktrees_excluded",
|
||||
"source_candidate_type": "gitea_repo_exists_with_momo_lineage_conflict",
|
||||
"canonical_source_ref": "gitea:wooo/ewoooc:main",
|
||||
"canonical_source_sha": "f3e412cd211f5e4601204b256aeb95eae073b441",
|
||||
"source_probe_status": "remote_main_verified_https_and_ssh",
|
||||
"source_warnings": [
|
||||
"local_ewoooc_dev_has_uncommitted_changes_excluded",
|
||||
"momo_worktrees_have_stale_or_dirty_refs_excluded"
|
||||
],
|
||||
"source_preflight_ready": true,
|
||||
"canonical_source_ready": true,
|
||||
"github_collision_preflight_ready": false,
|
||||
"create_private_repo_apply_ready": false,
|
||||
"refs_sync_apply_ready": false,
|
||||
"controlled_apply_ready": false,
|
||||
"blockers": [
|
||||
"github_account_suspended_403",
|
||||
"github_create_repo_channel_unavailable",
|
||||
"github_refs_sync_channel_unavailable"
|
||||
],
|
||||
"next_action": "restore GitHub write channel, create private repo, then normal-push gitea wooo/ewoooc main"
|
||||
},
|
||||
{
|
||||
"github_repo": "owenhytsai/bitan-pharmacy",
|
||||
"github_readback_status": "api_403_account_suspended",
|
||||
"target_selector": "github_owner=owenhytsai repo=bitan-pharmacy source_candidate=local_internal_remote_snapshot",
|
||||
"source_resolution_status": "ready_internal_remote_main_selected_dirty_worktree_excluded",
|
||||
"source_candidate_type": "internal_remote_snapshot_candidate",
|
||||
"canonical_source_ref": "internal-origin:bitan-pharmacy:main",
|
||||
"canonical_source_sha": "e122c8cbd9522999fd9844c2b63790fadcc89c20",
|
||||
"source_probe_status": "remote_main_verified",
|
||||
"source_warnings": [
|
||||
"local_worktree_has_64_tracked_changes_excluded",
|
||||
"local_worktree_has_untracked_changes_excluded"
|
||||
],
|
||||
"source_preflight_ready": true,
|
||||
"canonical_source_ready": true,
|
||||
"github_collision_preflight_ready": false,
|
||||
"create_private_repo_apply_ready": false,
|
||||
"refs_sync_apply_ready": false,
|
||||
"controlled_apply_ready": false,
|
||||
"blockers": [
|
||||
"github_account_suspended_403",
|
||||
"github_create_repo_channel_unavailable",
|
||||
"github_refs_sync_channel_unavailable"
|
||||
],
|
||||
"next_action": "restore GitHub write channel, create private repo, then normal-push internal bitan-pharmacy main; freeze dirty worktree separately if those changes must be backed up"
|
||||
},
|
||||
{
|
||||
"github_repo": "owenhytsai/tsenyang-website",
|
||||
"github_readback_status": "api_403_account_suspended",
|
||||
"target_selector": "github_owner=owenhytsai repo=tsenyang-website source_candidate=local_internal_remote_snapshot",
|
||||
"source_resolution_status": "ready_internal_remote_main_selected_dirty_worktree_excluded",
|
||||
"source_candidate_type": "internal_remote_snapshot_candidate",
|
||||
"canonical_source_ref": "internal-origin:tsenyang-website:main",
|
||||
"canonical_source_sha": "b369ed8cb6666e8ddaa2d31e1418a02794fdea9d",
|
||||
"source_probe_status": "remote_main_verified",
|
||||
"source_warnings": [
|
||||
"local_worktree_has_29_tracked_changes_excluded",
|
||||
"local_worktree_has_untracked_outputs_excluded"
|
||||
],
|
||||
"source_preflight_ready": true,
|
||||
"canonical_source_ready": true,
|
||||
"github_collision_preflight_ready": false,
|
||||
"create_private_repo_apply_ready": false,
|
||||
"refs_sync_apply_ready": false,
|
||||
"controlled_apply_ready": false,
|
||||
"blockers": [
|
||||
"github_account_suspended_403",
|
||||
"github_create_repo_channel_unavailable",
|
||||
"github_refs_sync_channel_unavailable"
|
||||
],
|
||||
"next_action": "restore GitHub write channel, create private repo, then normal-push internal tsenyang-website main; freeze dirty worktree separately if those changes must be backed up"
|
||||
},
|
||||
{
|
||||
"github_repo": "owenhytsai/VibeWork",
|
||||
"github_readback_status": "api_403_account_suspended",
|
||||
"target_selector": "github_owner=owenhytsai repo=VibeWork source_candidate=wooo/vibework",
|
||||
"source_resolution_status": "ready_gitea_main_selected_local_divergence_excluded",
|
||||
"source_candidate_type": "gitea_repo_exists_local_worktree_diverged",
|
||||
"canonical_source_ref": "gitea:wooo/vibework:main",
|
||||
"canonical_source_sha": "76a4ee15026af278a3660ad4b4547e9308b107be",
|
||||
"source_probe_status": "remote_main_verified",
|
||||
"source_warnings": [
|
||||
"local_vibework_head_48275cc52be79107e887147d3fe10310a887afe9_excluded",
|
||||
"local_tracked_dirty_count_0"
|
||||
],
|
||||
"source_preflight_ready": true,
|
||||
"canonical_source_ready": true,
|
||||
"github_collision_preflight_ready": false,
|
||||
"create_private_repo_apply_ready": false,
|
||||
"refs_sync_apply_ready": false,
|
||||
"controlled_apply_ready": false,
|
||||
"blockers": [
|
||||
"github_account_suspended_403",
|
||||
"github_create_repo_channel_unavailable",
|
||||
"github_refs_sync_channel_unavailable"
|
||||
],
|
||||
"next_action": "restore GitHub write channel, create private repo, then normal-push gitea wooo/vibework main; handle local VibeWork divergence separately"
|
||||
},
|
||||
{
|
||||
"github_repo": "owenhytsai/agent-bounty-protocol",
|
||||
"github_readback_status": "api_403_account_suspended",
|
||||
"target_selector": "github_owner=owenhytsai repo=agent-bounty-protocol source_candidate=wooo/agent-bounty-protocol",
|
||||
"source_resolution_status": "ready_gitea_main_selected_no_runtime_apply",
|
||||
"source_candidate_type": "gitea_repo_exists_high_risk_runtime_surface",
|
||||
"canonical_source_ref": "gitea:wooo/agent-bounty-protocol:main",
|
||||
"canonical_source_sha": "b7a733f44f4f645dd21a9b4a9075b89c4a324f64",
|
||||
"source_probe_status": "remote_main_verified",
|
||||
"source_warnings": [
|
||||
"local_agent_bounty_head_0601df8bd9c0aaedb9ce3a226a6f1aeca645ca0a_excluded",
|
||||
"runtime_surface_not_applied_by_backup_sync"
|
||||
],
|
||||
"source_preflight_ready": true,
|
||||
"canonical_source_ready": true,
|
||||
"github_collision_preflight_ready": false,
|
||||
"create_private_repo_apply_ready": false,
|
||||
"refs_sync_apply_ready": false,
|
||||
"controlled_apply_ready": false,
|
||||
"blockers": [
|
||||
"github_account_suspended_403",
|
||||
"github_create_repo_channel_unavailable",
|
||||
"github_refs_sync_channel_unavailable"
|
||||
],
|
||||
"next_action": "restore GitHub write channel, create private repo, then normal-push gitea wooo/agent-bounty-protocol main without runtime apply"
|
||||
}
|
||||
],
|
||||
"still_forbidden": [
|
||||
"secret_value",
|
||||
"token_value",
|
||||
"private_key",
|
||||
"cookie_or_session",
|
||||
"authorization_header",
|
||||
"private_clone_url_credential",
|
||||
"repo_archive",
|
||||
"git_object_pack",
|
||||
"force_push",
|
||||
"delete_refs",
|
||||
"tag_rewrite",
|
||||
"repo_delete",
|
||||
"github_primary_switch",
|
||||
"public_visibility",
|
||||
"raw_runtime_secret_volume",
|
||||
"unrelated_history_merge"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user