fix(runner): surface harbor repair closure blocker
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 37s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled

This commit is contained in:
Your Name
2026-06-30 21:24:21 +08:00
parent 4ccd800c1b
commit f376ff5de3
3 changed files with 83 additions and 7 deletions

View File

@@ -1,3 +1,18 @@
## 2026-06-30 — 21:23 Harbor 110 repair `awoooi-host` runner blocker closure readback
**照主線修正的問題**
- Live public queue 已由 `4ccd800c1` 的 reader 回 `status=blocked_harbor_110_repair_no_matching_runner`:最新 CD `#4075` runningHarbor repair `#4076` Waiting`latest_visible_harbor_110_repair_no_matching_runner_label=awoooi-host`registry public / internal `/v2/` 仍為 502。
- 現有 `verify-awoooi-non110-cd-closure.py` 雖讀到 queue status仍降級成 generic `blocked_no_matching_online_runner`;本輪新增專用 closure blocker `harbor_110_repair_no_matching_runner` 與 next action `restore_awoooi_host_runner_control_path_without_legacy_or_generic_labels_then_rerun_harbor_110_repair_queue_readback`
- Live closure verifier 現在回 `status=blocked_harbor_110_repair_no_matching_runner``ordered_completion_percent=33``evidence_completion_percent=67`;下一步明確是恢復 `awoooi-host` 110 repair control path而不是泛化重跑 queue 或宣稱 Harbor repair 成功。
**驗證**
- `py_compile ops/runner/verify-awoooi-non110-cd-closure.py ops/runner/test_verify_awoooi_non110_cd_closure.py` 通過。
- `pytest ops/runner/test_verify_awoooi_non110_cd_closure.py -q` 通過8 passed
- `ruff check ops/runner/verify-awoooi-non110-cd-closure.py ops/runner/test_verify_awoooi_non110_cd_closure.py` 通過。
- Live `verify-awoooi-non110-cd-closure.py --json` 回專用 `blocked_harbor_110_repair_no_matching_runner`,且 operation boundaries 全部維持 no-write / no-secret / no-dispatch。
**邊界**:只改 closure verifier / tests / LOGBOOK110 SSH read-only metadata probe timeout 後已中止,未讀 `.runner`、secret、token、`.env`、raw sessions、SQLite 或 auth未使用 GitHub / `gh` / GitHub API未 workflow_dispatch未 SSH 寫入、Docker / Nginx / K3s / DB / firewall runtime 寫入或服務重啟。
## 2026-06-30 — 21:13 Priority work-order readback 對齊 reboot SLO fail-closed 真相
**照主線修正的問題**

View File

@@ -24,18 +24,29 @@ def _load_module():
return module
def _queue(*, no_matching: bool) -> dict:
def _queue(*, no_matching: bool, harbor_110_no_matching: bool = False) -> dict:
return {
"schema_version": "awoooi_public_gitea_actions_queue_readback_v1",
"status": (
"blocked_harbor_110_repair_no_matching_runner"
if harbor_110_no_matching
else (
"blocked_no_matching_online_runner"
if no_matching
else "no_matching_runner_not_visible"
)
),
"readback": {
"no_matching_online_runner_visible": no_matching,
"latest_visible_no_matching_runner_label": (
"awoooi-non110-ubuntu" if no_matching else ""
"awoooi-host"
if harbor_110_no_matching
else "awoooi-non110-ubuntu"
if no_matching
else ""
),
"latest_visible_harbor_110_repair_no_matching_runner_label": (
"awoooi-host" if harbor_110_no_matching else ""
),
},
"operation_boundaries": {
@@ -174,6 +185,31 @@ def test_closure_verifier_blocks_queue_after_runner_ready() -> None:
assert "public_queue_still_has_no_matching_online_runner" in payload["blockers"]
def test_closure_verifier_prioritizes_harbor_110_runner_label_blocker() -> None:
module = _load_module()
payload = module.build_closure_verifier(
readiness_text=_readiness(ready=True),
queue=_queue(no_matching=True, harbor_110_no_matching=True),
production_workbench=_workbench(image_current=False, governance_ready=False),
)
assert payload["status"] == "blocked_harbor_110_repair_no_matching_runner"
assert "harbor_110_repair_no_matching_runner" in payload["blockers"]
assert "public_queue_still_has_no_matching_online_runner" not in payload[
"blockers"
]
assert payload["readback"]["harbor_110_repair_no_matching_runner"] is True
assert (
payload["readback"]["harbor_110_repair_no_matching_runner_label"]
== "awoooi-host"
)
assert payload["progress"]["next_blocked_step_id"] == "public_queue_runner_match"
assert "awoooi_host_runner_control_path" in payload["next_actions"][0]
assert "awoooi_host_runner_control_path" in payload["progress"][
"next_blocked_step_action"
]
def test_closure_verifier_uses_deploy_snapshot_when_readiness_file_missing() -> None:
module = _load_module()
payload = module.build_closure_verifier(

View File

@@ -176,6 +176,7 @@ def _build_ordered_steps(
*,
readiness: dict[str, Any],
no_matching_runner_visible: bool,
queue_runner_match_next_action: str,
production_workbench_present: bool,
production_image_tag_matches_main: bool,
production_governance_fields_present: bool,
@@ -208,7 +209,7 @@ def _build_ordered_steps(
"id": "public_queue_runner_match",
"title": "public Gitea queue no longer shows no-matching-runner",
"evidence_ready": not no_matching_runner_visible,
"next_action": "rerun_public_queue_readback_until_no_matching_runner_is_absent",
"next_action": queue_runner_match_next_action,
},
{
"id": "production_workbench_readback",
@@ -300,11 +301,26 @@ def build_closure_verifier(
if isinstance(queue.get("operation_boundaries"), dict)
else {}
)
queue_status = str(queue.get("status") or "")
production = _production_summary(production_workbench)
no_matching_runner_visible = (
queue_readback.get("no_matching_online_runner_visible") is True
)
harbor_110_repair_no_matching_runner_label = str(
queue_readback.get("latest_visible_harbor_110_repair_no_matching_runner_label")
or ""
)
harbor_110_repair_no_matching_runner = (
queue_status == "blocked_harbor_110_repair_no_matching_runner"
or bool(harbor_110_repair_no_matching_runner_label)
)
queue_runner_match_next_action = (
"restore_awoooi_host_runner_control_path_without_legacy_or_generic_labels_"
"then_rerun_harbor_110_repair_queue_readback"
if harbor_110_repair_no_matching_runner
else "rerun_public_queue_readback_until_no_matching_runner_is_absent"
)
production_workbench_present = bool(production)
production_image_tag_matches_main = _production_image_tag_current(production)
production_governance_fields_present = (
@@ -318,7 +334,9 @@ def build_closure_verifier(
blockers.append("non110_runner_not_ready")
if readiness["raw_runner_registration_read"]:
blockers.append("raw_runner_registration_read_not_allowed")
if no_matching_runner_visible:
if harbor_110_repair_no_matching_runner:
blockers.append("harbor_110_repair_no_matching_runner")
elif no_matching_runner_visible:
blockers.append("public_queue_still_has_no_matching_online_runner")
if not production_workbench_present:
blockers.append("production_workbench_readback_missing")
@@ -333,6 +351,8 @@ def build_closure_verifier(
status = "blocked_non110_runner_not_ready"
elif "raw_runner_registration_read_not_allowed" in blockers:
status = "blocked_secret_boundary_violation"
elif "harbor_110_repair_no_matching_runner" in blockers:
status = "blocked_harbor_110_repair_no_matching_runner"
elif "public_queue_still_has_no_matching_online_runner" in blockers:
status = "blocked_no_matching_online_runner"
elif "production_workbench_readback_missing" in blockers:
@@ -347,6 +367,7 @@ def build_closure_verifier(
ordered_steps = _build_ordered_steps(
readiness=readiness,
no_matching_runner_visible=no_matching_runner_visible,
queue_runner_match_next_action=queue_runner_match_next_action,
production_workbench_present=production_workbench_present,
production_image_tag_matches_main=production_image_tag_matches_main,
production_governance_fields_present=production_governance_fields_present,
@@ -382,6 +403,12 @@ def build_closure_verifier(
"public_queue_latest_no_matching_runner_label": str(
queue_readback.get("latest_visible_no_matching_runner_label") or ""
),
"harbor_110_repair_no_matching_runner": (
harbor_110_repair_no_matching_runner
),
"harbor_110_repair_no_matching_runner_label": (
harbor_110_repair_no_matching_runner_label
),
"production_workbench_present": production_workbench_present,
"production_workbench_source_count": _int(production.get("source_count")),
"production_deploy_image_tag_matches_main": (
@@ -417,9 +444,7 @@ def build_closure_verifier(
readiness["safe_next_step"]
if not readiness["ready"]
else "",
"rerun_public_queue_readback_until_no_matching_runner_is_absent"
if no_matching_runner_visible
else "",
queue_runner_match_next_action if no_matching_runner_visible else "",
"read_production_delivery_workbench_after_deploy"
if not production_workbench_present
else "",