{ "schema_version": "source_control_primary_rollback_adr_v1", "status": "draft_waiting_owner_review", "date": "2026-05-13", "mode": "rollback_adr_only", "runtime_execution_authorized": false, "source_indexes": [ "docs/security/source-control-primary-readiness-gate.snapshot.json", "docs/security/source-control-ref-truth-classification.snapshot.json", "docs/security/source-control-workflow-secret-name-inventory.snapshot.json", "docs/security/source-control-workflow-secret-name-export-request.snapshot.json", "docs/security/source-control-approval-board.snapshot.json", "docs/security/security-followup-runtime-gate.snapshot.json", "docs/security/security-rollout-policy.snapshot.json" ], "summary": { "candidate_repo_count": 8, "in_scope_repo_count": 7, "external_scope_count": 1, "repo_rollback_plan_count": 7, "owner_approved_count": 0, "dry_run_completed_count": 0, "active_cutover_count": 0, "rollback_execution_authorized": false, "github_primary_switch_authorized": false, "gitea_disable_authorized": false, "action_buttons_allowed": false }, "rollback_principles": [ "GitHub primary 是長期方向,但每個 repo 必須先有 owner-approved rollback plan 才能進入 cutover review。", "Gitea 在 cutover 前後都必須保留為本地 mirror / fallback,不得因 GitHub primary 準備而停用、刪除或封存。", "Rollback ADR 只定義人工決策、驗證窗口與回退條件;不授權任何 refs sync、primary switch 或 webhook 修改。", "任何回退都必須有新的 runtime gate、人工批准與 evidence snapshot,不得由本 ADR 自動觸發。", "初期只做 observe / approval_required,不把缺 LOW / MEDIUM evidence 變成 production blocker。" ], "cutover_preconditions": [ { "gate_id": "gitea_authenticated_inventory_approved", "title": "Gitea private/internal 全量 inventory 已完成", "required_evidence": [ "Gitea authenticated inventory 或 redacted admin export status=ok", "確認所有 private/internal repos 都在 migration matrix", "只保存 token_present=true/false,不保存 token value" ], "current_status": "blocked", "execution_authorized": false }, { "gate_id": "refs_truth_and_parity_approved", "title": "refs truth / branch-tag parity 已由 owner 批准", "required_evidence": [ "main/dev 真相來源已人工判定", "release tags 保留 / 棄用決策完成", "deprecated refs 已由 repo owner review" ], "current_status": "waiting_owner_review", "execution_authorized": false }, { "gate_id": "workflow_secret_export_accepted", "title": "workflow / runner / webhook / secret name redacted export 已驗收", "required_evidence": [ "S4.3 export request 對應的 webhook、runner、deploy key、branch protection 與 repository secret name parity evidence 已補齊", "任何 secret value、token value、private key 或 webhook secret 都未被保存", "GitHub hosted runner 額度風險與 self-hosted runner owner 已確認" ], "current_status": "draft_only", "execution_authorized": false }, { "gate_id": "owner_visibility_canonical_approved", "title": "owner / visibility / canonical 已批准", "required_evidence": [ "7 個 in-scope repos 都有 owner approval", "not_found_or_private repo 已明確判定建立、取得權限或排除", "ewoooc / momo-pro-system canonical 關係已定案" ], "current_status": "waiting_owner_review", "execution_authorized": false }, { "gate_id": "rollback_owner_and_monitoring_approved", "title": "rollback owner、監控窗口與通知責任已批准", "required_evidence": [ "每個 repo 都有 rollback owner 與值班窗口", "cutover 後 1h / 24h 的驗證項目已明確", "失敗時的人工停損與回退 decision record 格式已確認" ], "current_status": "draft_only", "execution_authorized": false } ], "repo_rollback_plans": [ { "repo_key": "awoooi", "github_repo": "owenhytsai/awoooi", "source_key": "wooo/awoooi", "scope_status": "in_scope", "risk": "HIGH", "rollback_state": "draft_waiting_owner_review", "primary_ready": false, "fallback_role": "Gitea remains protected local fallback until owner-approved cutover and rollback drill are complete.", "required_owner_decisions": [ "main/dev 真相來源", "production deploy workflow canonical", "webhook single-sender policy", "rollback owner" ], "rollback_evidence_required": [ "main SHA / tag parity evidence", "deployment marker workflow evidence", "runner owner / self-hosted evidence", "repository secret name parity evidence", "post-cutover health and deploy verification checklist" ], "rollback_triggers": [ "production deploy workflow fails after primary switch", "duplicate webhook causes duplicate deploy or duplicate notification", "required runner label unavailable", "secret name parity gap blocks deployment" ], "manual_recovery_outline": [ "Freeze additional refs changes and record decision in approval audit.", "Keep Gitea fallback intact and route deploy decision back to approved source after runtime gate approval.", "Collect post-rollback evidence before re-entering primary readiness review." ], "execution_authorized": false, "still_forbidden": [ "switch_github_primary", "disable_gitea", "sync_refs", "modify_workflow", "move_secret_values" ] }, { "repo_key": "clawbot-v5", "github_repo": "owenhytsai/clawbot-v5", "source_key": "wooo/clawbot-v5", "scope_status": "in_scope", "risk": "MEDIUM", "rollback_state": "draft_waiting_owner_review", "primary_ready": false, "fallback_role": "Gitea remains fallback until tag retention and refs truth are approved.", "required_owner_decisions": [ "main 真相來源", "Gitea-only tag 保留或棄用", "是否需要 workflow / secret inventory", "rollback owner" ], "rollback_evidence_required": [ "branch/tag parity evidence", "owner-approved tag policy", "repository secret name parity or no-secret attestation", "post-cutover smoke check" ], "rollback_triggers": [ "GitHub target misses owner-approved tag", "automation expects a Gitea-only ref", "repo owner rejects target canonical decision" ], "manual_recovery_outline": [ "Pause primary review and keep Gitea as canonical fallback.", "Ask owner to classify missing refs before any retry.", "Require new approval record before resuming cutover preparation." ], "execution_authorized": false, "still_forbidden": [ "push_refs", "delete_refs", "switch_github_primary", "delete_gitea_repo" ] }, { "repo_key": "wooo-aiops", "github_repo": "owenhytsai/wooo-aiops", "source_key": "wooo/wooo-aiops", "scope_status": "in_scope", "risk": "MEDIUM", "rollback_state": "draft_waiting_owner_review", "primary_ready": false, "fallback_role": "Gitea remains fallback until GitHub-only refs and workflow ownership are resolved.", "required_owner_decisions": [ "GitHub-only branch / tag 來源", "webhook owner", "runner / workflow owner", "rollback owner" ], "rollback_evidence_required": [ "GitHub-only refs classification", "webhook redacted export", "runner owner evidence", "repository secret name parity evidence" ], "rollback_triggers": [ "GitHub-only refs are later classified as active source", "webhook route changes produce duplicate events", "workflow runner mismatch increases hosted runner usage unexpectedly" ], "manual_recovery_outline": [ "Stop cutover review and preserve both remote states.", "Route owner decision back through source-control approval board.", "Re-run readiness gate only after redacted evidence is updated." ], "execution_authorized": false, "still_forbidden": [ "delete_github_only_refs", "modify_webhook", "switch_github_primary", "force_push" ] }, { "repo_key": "wooo-infra-config", "github_repo": "owenhytsai/wooo-infra-config", "source_key": "wooo/wooo-infra-config", "scope_status": "in_scope", "risk": "MEDIUM", "rollback_state": "draft_waiting_owner_review", "primary_ready": false, "fallback_role": "Gitea and internal 110 remote roles stay unchanged until infra owner signs off.", "required_owner_decisions": [ "110 internal remote purpose", "deploy key owner", "infra secret name owner", "rollback owner" ], "rollback_evidence_required": [ "internal remote purpose decision", "deploy key redacted inventory", "branch protection / required check export", "infra secret name parity evidence" ], "rollback_triggers": [ "internal remote is still an active source", "deploy key ownership is ambiguous", "required status checks are missing on GitHub target", "infra secret parity gap blocks validation" ], "manual_recovery_outline": [ "Keep current infra source untouched and do not delete remotes.", "Escalate to infra owner for manual source-of-truth decision.", "Resume only after a new redacted export snapshot is committed." ], "execution_authorized": false, "still_forbidden": [ "delete_remote", "move_secret_values", "export_private_key", "switch_github_primary" ] }, { "repo_key": "ewoooc", "github_repo": "owenhytsai/ewoooc", "source_key": "wooo/ewoooc / root/momo-pro-system / momo working trees", "scope_status": "in_scope", "risk": "HIGH", "rollback_state": "draft_waiting_owner_review", "primary_ready": false, "fallback_role": "No primary decision until canonical repository and unrelated history risk are resolved.", "required_owner_decisions": [ "GitHub target access or creation decision", "ewoooc / momo-pro-system canonical decision", "unrelated history handling", "rollback owner" ], "rollback_evidence_required": [ "canonical repo decision record", "server-side refs diff", "workflow / secret name redacted export", "post-cutover web and deploy health checks" ], "rollback_triggers": [ "target repo access remains unresolved", "canonical decision conflicts with local lineage evidence", "unrelated histories cannot be reconciled without data loss risk" ], "manual_recovery_outline": [ "Keep all existing working trees untouched.", "Do not create or merge GitHub target automatically.", "Require canonical owner approval before any new cutover attempt." ], "execution_authorized": false, "still_forbidden": [ "auto_create_repo", "auto_merge_unrelated_histories", "delete_working_tree", "switch_github_primary" ] }, { "repo_key": "bitan-pharmacy", "github_repo": "owenhytsai/bitan-pharmacy", "source_key": "bitan-pharmacy", "scope_status": "in_scope", "risk": "MEDIUM", "rollback_state": "draft_waiting_owner_review", "primary_ready": false, "fallback_role": "Current local / 110 source remains evidence source until active-state and GitHub target are approved.", "required_owner_decisions": [ "repo active or archive decision", "GitHub target decision", "secret name / deploy owner decision", "rollback owner" ], "rollback_evidence_required": [ "GitHub target visibility decision", "repository secret name parity or no-secret attestation", "active-state owner decision", "post-cutover smoke check if active" ], "rollback_triggers": [ "repo is confirmed inactive or out of scope", "GitHub target cannot be verified", "owner cannot confirm deploy / secret requirements" ], "manual_recovery_outline": [ "Keep repo out of primary cutover queue until owner decision exists.", "Do not create GitHub target automatically.", "If inactive, record owner decision rather than deleting source." ], "execution_authorized": false, "still_forbidden": [ "auto_create_repo", "push_refs", "delete_110_remote", "switch_github_primary" ] }, { "repo_key": "tsenyang-website", "github_repo": "owenhytsai/tsenyang-website", "source_key": "tsenyang-website", "scope_status": "in_scope", "risk": "MEDIUM", "rollback_state": "draft_waiting_owner_review", "primary_ready": false, "fallback_role": "Current local / 110 source remains evidence source until active-state and GitHub target are approved.", "required_owner_decisions": [ "repo active or archive decision", "GitHub target decision", "secret name / deploy owner decision", "rollback owner" ], "rollback_evidence_required": [ "GitHub target visibility decision", "repository secret name parity or no-secret attestation", "active-state owner decision", "post-cutover smoke check if active" ], "rollback_triggers": [ "repo is confirmed inactive or out of scope", "GitHub target cannot be verified", "owner cannot confirm deploy / secret requirements" ], "manual_recovery_outline": [ "Keep repo out of primary cutover queue until owner decision exists.", "Do not create GitHub target automatically.", "If inactive, record owner decision rather than deleting source." ], "execution_authorized": false, "still_forbidden": [ "auto_create_repo", "push_refs", "delete_110_remote", "switch_github_primary" ] }, { "repo_key": "open-design", "github_repo": "nexu-io/open-design", "source_key": "open-design", "scope_status": "external_scope_review", "risk": "LOW", "rollback_state": "scope_review_only", "primary_ready": false, "fallback_role": "Not in AWOOOI primary cutover scope until ownership is confirmed.", "required_owner_decisions": [ "scope ownership decision" ], "rollback_evidence_required": [ "scope review evidence" ], "rollback_triggers": [ "repo is later confirmed in scope and needs a separate ADR" ], "manual_recovery_outline": [ "Keep out of primary queue.", "Create a separate in-scope approval item if ownership changes." ], "execution_authorized": false, "still_forbidden": [ "加入 primary cutover queue", "修改 repo visibility", "sync refs" ] } ], "rollback_triggers": [ "main/dev SHA 或 tag parity 與 owner-approved truth 不一致", "workflow、webhook、runner、deploy key、branch protection 或 repository secret name parity evidence 不完整", "GitHub hosted runner 使用量或 billing risk 超出 owner-approved 範圍", "deploy marker、release workflow 或 required status check 在 cutover 後失敗", "duplicate webhook 造成重複部署、重複通知或 approval queue 重複事件", "owner / visibility / canonical decision 被撤回或出現衝突", "post-cutover 1h 或 24h validation window 未通過" ], "validation_windows": [ { "window_id": "pre_cutover_freeze_review", "title": "切換前 freeze review", "required_checks": [ "確認 refs truth、workflow / secret export、owner / visibility / canonical、rollback owner 全部已批准", "確認沒有 unresolved HIGH risk blocker", "確認 Gitea fallback 保持可用" ], "failure_handling": "任何缺口都只回到 approval review,不執行 cutover。", "execution_authorized": false }, { "window_id": "post_cutover_one_hour_observe", "title": "切換後 1 小時觀察窗口", "required_checks": [ "deploy workflow / required checks 成功", "webhook 沒有重複事件", "runner 使用符合 owner-approved self-hosted / hosted policy", "核心服務健康與告警量未異常升高" ], "failure_handling": "只建立 rollback approval candidate;不得自動切回或自動改 webhook。", "execution_authorized": false }, { "window_id": "post_cutover_twenty_four_hour_review", "title": "切換後 24 小時 review", "required_checks": [ "refs、deploy、webhook、runner、secret name parity 沒有新增 drift", "AwoooP audit evidence 完整", "repo owner 確認可維持 GitHub primary 或要求 rollback review" ], "failure_handling": "若未通過,保留 Gitea fallback 並等待人工 rollback decision record。", "execution_authorized": false } ], "acceptance_rules": [ "本 ADR 草案完成不代表任何 repo 可切 GitHub primary。", "每個 in-scope repo 必須有 owner-approved rollback plan、pre-cutover evidence、post-cutover validation window 與 rollback owner。", "任何 rollback 或 primary switch 都必須另有 runtime gate 與人工批准,不得由本 snapshot 自動觸發。", "Gitea fallback 不得在 24h review 完成前停用、刪除、封存或降級。", "任何 secret、token、cookie、private key、webhook secret 或 runner token 都不得保存。" ], "forbidden_actions": [ "switch_github_primary", "execute_rollback", "create_github_repo", "change_repo_visibility", "sync_git_refs", "delete_git_refs", "force_push", "modify_webhook", "modify_workflow", "modify_branch_protection", "move_secret_values", "disable_gitea", "delete_or_archive_gitea_repo", "add_action_button" ] }