diff --git a/apps/api/src/services/delivery_closure_workbench.py b/apps/api/src/services/delivery_closure_workbench.py index ef4ee325..3d906fe3 100644 --- a/apps/api/src/services/delivery_closure_workbench.py +++ b/apps/api/src/services/delivery_closure_workbench.py @@ -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") ), diff --git a/apps/api/src/services/github_target_private_backup_evidence_gate.py b/apps/api/src/services/github_target_private_backup_evidence_gate.py index d0b82a62..a3257e3f 100644 --- a/apps/api/src/services/github_target_private_backup_evidence_gate.py +++ b/apps/api/src/services/github_target_private_backup_evidence_gate.py @@ -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") diff --git a/apps/api/tests/test_delivery_closure_workbench_api.py b/apps/api/tests/test_delivery_closure_workbench_api.py index 27f4c960..04e182d6 100644 --- a/apps/api/tests/test_delivery_closure_workbench_api.py +++ b/apps/api/tests/test_delivery_closure_workbench_api.py @@ -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()) diff --git a/apps/api/tests/test_github_target_private_backup_evidence_gate.py b/apps/api/tests/test_github_target_private_backup_evidence_gate.py index 405b65ea..d032e924 100644 --- a/apps/api/tests/test_github_target_private_backup_evidence_gate.py +++ b/apps/api/tests/test_github_target_private_backup_evidence_gate.py @@ -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 diff --git a/apps/api/tests/test_github_target_private_backup_evidence_gate_api.py b/apps/api/tests/test_github_target_private_backup_evidence_gate_api.py index 6fdbd5e1..3568fe2d 100644 --- a/apps/api/tests/test_github_target_private_backup_evidence_gate_api.py +++ b/apps/api/tests/test_github_target_private_backup_evidence_gate_api.py @@ -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 diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 2cacfa07..cecadf9a 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -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。 diff --git a/docs/security/github-target-controlled-execution-preflight.snapshot.json b/docs/security/github-target-controlled-execution-preflight.snapshot.json index 26597852..d5ae7a77 100644 --- a/docs/security/github-target-controlled-execution-preflight.snapshot.json +++ b/docs/security/github-target-controlled-execution-preflight.snapshot.json @@ -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" + ] }