fix(agent): write back stale harbor repair receipt
Some checks failed
CD Pipeline / workflow-shape (push) Has been cancelled
CD Pipeline / cancel-stale-cd (push) Has been cancelled
CD Pipeline / tests (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled

This commit is contained in:
Your Name
2026-07-01 13:38:42 +08:00
parent bc46edc01c
commit d597e11244
3 changed files with 205 additions and 0 deletions

View File

@@ -268,6 +268,12 @@ def validate_harbor_registry_controlled_recovery_receipt(
"gitea_queue_harbor_110_jobs_payload_classifier": gitea_queue[
"harbor_110_repair_jobs_payload_classifier"
],
"gitea_queue_harbor_110_repair_visible_running_jobs_api_stale": (
gitea_queue["harbor_110_repair_visible_running_jobs_api_stale"]
),
"gitea_queue_current_cd_waiting_behind_harbor_110_repair_running": (
gitea_queue["current_cd_waiting_behind_harbor_110_repair_running"]
),
"gitea_queue_latest_cd_run_status": gitea_queue[
"latest_visible_cd_run_status"
],
@@ -407,6 +413,12 @@ def _control_path_readiness(
queue_jobs_cross_workflow = bool(
gitea_queue["harbor_110_repair_jobs_cross_workflow_mismatch"]
)
queue_harbor_running_jobs_api_stale = bool(
gitea_queue["harbor_110_repair_visible_running_jobs_api_stale"]
)
current_cd_waiting_behind_harbor_running = bool(
gitea_queue["current_cd_waiting_behind_harbor_110_repair_running"]
)
queue_remote_control_unavailable = bool(
gitea_queue["harbor_110_repair_remote_control_channel_unavailable"]
)
@@ -437,6 +449,8 @@ def _control_path_readiness(
awoooi_host_unavailable = (
queue_no_matching_runner
or queue_jobs_stale
or queue_harbor_running_jobs_api_stale
or current_cd_waiting_behind_harbor_running
or current_cd_no_matching_runner
or controlled_profile_no_matching_runner
)
@@ -454,6 +468,10 @@ def _control_path_readiness(
queue_no_matching_runner=queue_no_matching_runner,
queue_jobs_stale=queue_jobs_stale,
queue_jobs_cross_workflow=queue_jobs_cross_workflow,
queue_harbor_running_jobs_api_stale=queue_harbor_running_jobs_api_stale,
current_cd_waiting_behind_harbor_running=(
current_cd_waiting_behind_harbor_running
),
queue_remote_control_unavailable=queue_remote_control_unavailable,
cd_harbor_repair_requires_110_lane=cd_harbor_repair_requires_110_lane,
cd_harbor_repair_blocked_by_awoooi_host=(
@@ -516,6 +534,12 @@ def _control_path_readiness(
],
"harbor_110_repair_jobs_stale_or_mismatched": queue_jobs_stale,
"harbor_110_repair_jobs_cross_workflow_mismatch": queue_jobs_cross_workflow,
"harbor_110_repair_visible_running_jobs_api_stale": (
queue_harbor_running_jobs_api_stale
),
"current_cd_waiting_behind_harbor_110_repair_running": (
current_cd_waiting_behind_harbor_running
),
"harbor_110_repair_remote_control_channel_unavailable": (
queue_remote_control_unavailable
),
@@ -562,6 +586,8 @@ def _control_path_signal_ids(
queue_no_matching_runner: bool,
queue_jobs_stale: bool,
queue_jobs_cross_workflow: bool,
queue_harbor_running_jobs_api_stale: bool,
current_cd_waiting_behind_harbor_running: bool,
queue_remote_control_unavailable: bool,
cd_harbor_repair_requires_110_lane: bool,
cd_harbor_repair_blocked_by_awoooi_host: bool,
@@ -589,6 +615,14 @@ def _control_path_signal_ids(
signal_ids.append("gitea_queue_harbor_110_repair_jobs_cross_workflow_mismatch")
if queue_jobs_stale:
signal_ids.append("gitea_queue_harbor_110_repair_jobs_stale_or_mismatched")
if queue_harbor_running_jobs_api_stale:
signal_ids.append(
"gitea_queue_harbor_110_repair_visible_running_jobs_api_stale"
)
if current_cd_waiting_behind_harbor_running:
signal_ids.append(
"gitea_queue_current_cd_waiting_behind_harbor_110_repair_running"
)
if queue_remote_control_unavailable:
signal_ids.append("gitea_queue_harbor_110_remote_control_channel_unavailable")
if cd_harbor_repair_blocked_by_awoooi_host:
@@ -1201,6 +1235,8 @@ def _gitea_queue_readback(value: Any) -> dict[str, Any]:
"harbor_110_repair_jobs_stale_or_mismatched": False,
"harbor_110_repair_jobs_cross_workflow_mismatch": False,
"harbor_110_repair_jobs_payload_classifier": "",
"harbor_110_repair_visible_running_jobs_api_stale": False,
"current_cd_waiting_behind_harbor_110_repair_running": False,
"harbor_110_repair_jobs_expected_names": [],
"harbor_110_repair_jobs_unexpected_names": [],
"harbor_110_repair_jobs_labels": [],
@@ -1362,6 +1398,14 @@ def _gitea_queue_readback(value: Any) -> dict[str, Any]:
or readback.get("harbor_110_repair_jobs_payload_classifier")
or ""
)
harbor_running_jobs_api_stale = bool(
rollups.get("harbor_110_repair_visible_running_jobs_api_stale") is True
or readback.get("harbor_110_repair_visible_running_jobs_api_stale") is True
)
current_cd_waiting_behind_harbor_running = bool(
rollups.get("current_cd_waiting_behind_harbor_110_repair_running") is True
or readback.get("current_cd_waiting_behind_harbor_110_repair_running") is True
)
boundary_violation = any(
operation_boundaries.get(flag) is True
for flag in (
@@ -1387,6 +1431,10 @@ def _gitea_queue_readback(value: Any) -> dict[str, Any]:
current_cd_harbor_repair_blocked_by_awoooi_host
),
controlled_profile_no_matching=controlled_profile_no_matching,
harbor_running_jobs_api_stale=harbor_running_jobs_api_stale,
current_cd_waiting_behind_harbor_running=(
current_cd_waiting_behind_harbor_running
),
cd_jobs_stale=cd_jobs_stale,
cd_jobs_head_sha_mismatch=cd_jobs_head_sha_mismatch,
cd_jobs_run_id_mismatch=cd_jobs_run_id_mismatch,
@@ -1406,6 +1454,10 @@ def _gitea_queue_readback(value: Any) -> dict[str, Any]:
current_cd_no_matching_label=current_cd_no_matching_label,
current_cd_waiting=current_cd_waiting,
controlled_profile_no_matching_labels=controlled_profile_no_matching_labels,
harbor_running_jobs_api_stale=harbor_running_jobs_api_stale,
current_cd_waiting_behind_harbor_running=(
current_cd_waiting_behind_harbor_running
),
blockers=blockers,
)
return {
@@ -1484,6 +1536,12 @@ def _gitea_queue_readback(value: Any) -> dict[str, Any]:
"harbor_110_repair_jobs_stale_or_mismatched": jobs_stale,
"harbor_110_repair_jobs_cross_workflow_mismatch": jobs_cross_workflow,
"harbor_110_repair_jobs_payload_classifier": harbor_jobs_payload_classifier,
"harbor_110_repair_visible_running_jobs_api_stale": (
harbor_running_jobs_api_stale
),
"current_cd_waiting_behind_harbor_110_repair_running": (
current_cd_waiting_behind_harbor_running
),
"harbor_110_repair_jobs_expected_names": _strings(
readback.get("harbor_110_repair_jobs_expected_names")
),
@@ -1519,6 +1577,8 @@ def _gitea_queue_normalized_classifier_fields(
current_cd_no_matching_label: str,
current_cd_waiting: bool,
controlled_profile_no_matching_labels: dict[str, str],
harbor_running_jobs_api_stale: bool,
current_cd_waiting_behind_harbor_running: bool,
blockers: list[str],
) -> list[dict[str, Any]]:
fields = [
@@ -1594,6 +1654,39 @@ def _gitea_queue_normalized_classifier_fields(
"raw_output_returned": False,
}
)
if harbor_running_jobs_api_stale:
fields.append(
{
"field_id": "harbor_110_repair_visible_running_jobs_api_stale",
"value": True,
"blockers": [
item
for item in blockers
if item
== "gitea_queue_harbor_110_repair_visible_running_jobs_api_stale"
],
"metadata_only": True,
"raw_output_returned": False,
}
)
if current_cd_waiting_behind_harbor_running:
fields.append(
{
"field_id": "current_cd_waiting_behind_harbor_110_repair_running",
"value": True,
"blockers": [
item
for item in blockers
if item
== (
"gitea_queue_current_cd_waiting_behind_harbor_110_"
"repair_running"
)
],
"metadata_only": True,
"raw_output_returned": False,
}
)
if controlled_profile_no_matching_labels:
fields.append(
{
@@ -1622,6 +1715,8 @@ def _gitea_queue_blockers(
current_cd_harbor_repair_requires_110_lane: bool,
current_cd_harbor_repair_blocked_by_awoooi_host: bool,
controlled_profile_no_matching: bool,
harbor_running_jobs_api_stale: bool,
current_cd_waiting_behind_harbor_running: bool,
cd_jobs_stale: bool,
cd_jobs_head_sha_mismatch: bool,
cd_jobs_run_id_mismatch: bool,
@@ -1668,12 +1763,22 @@ def _gitea_queue_blockers(
and not harbor_repair_remote_control_unavailable
and not harbor_repair_local_v2_unavailable
and not harbor_repair_public_v2_unavailable
and not harbor_running_jobs_api_stale
and not current_cd_waiting_behind_harbor_running
):
blockers.append("gitea_queue_harbor_110_repair_blocked")
if jobs_cross_workflow:
blockers.append("gitea_queue_harbor_110_repair_jobs_cross_workflow_mismatch")
if jobs_stale:
blockers.append("gitea_queue_harbor_110_repair_jobs_stale_or_mismatched")
if harbor_running_jobs_api_stale:
blockers.append(
"gitea_queue_harbor_110_repair_visible_running_jobs_api_stale"
)
if current_cd_waiting_behind_harbor_running:
blockers.append(
"gitea_queue_current_cd_waiting_behind_harbor_110_repair_running"
)
if boundary_violation:
blockers.append("gitea_queue_readback_boundary_violation")
return blockers

View File

@@ -621,6 +621,60 @@ def test_harbor_recovery_receipt_surfaces_cross_workflow_queue_payload() -> None
]
def test_harbor_recovery_receipt_surfaces_stale_harbor_running_blocker() -> None:
payload = validate_harbor_registry_controlled_recovery_receipt(
{
"public_registry_v2_http_status": 502,
"internal_registry_v2_http_status": 502,
"gitea_actions_queue_readback": (
_gitea_queue_cd_waiting_behind_stale_harbor_running()
),
}
)
assert (
"gitea_queue_harbor_110_repair_visible_running_jobs_api_stale"
in payload["active_blockers"]
)
assert (
"gitea_queue_current_cd_waiting_behind_harbor_110_repair_running"
in payload["active_blockers"]
)
queue = payload["readback"]["gitea_actions_queue"]
assert queue["harbor_110_repair_visible_running_jobs_api_stale"] is True
assert queue["current_cd_waiting_behind_harbor_110_repair_running"] is True
readiness = payload["readback"]["control_path_readiness"]
assert (
"gitea_queue_harbor_110_repair_visible_running_jobs_api_stale"
in readiness["signal_ids"]
)
assert (
"gitea_queue_current_cd_waiting_behind_harbor_110_repair_running"
in readiness["signal_ids"]
)
assert (
payload["rollups"][
"gitea_queue_harbor_110_repair_visible_running_jobs_api_stale"
]
is True
)
assert (
payload["rollups"][
"gitea_queue_current_cd_waiting_behind_harbor_110_repair_running"
]
is True
)
normalized = {
item["field_id"]: item for item in queue["normalized_classifier_fields"]
}
assert normalized["harbor_110_repair_visible_running_jobs_api_stale"][
"blockers"
] == ["gitea_queue_harbor_110_repair_visible_running_jobs_api_stale"]
assert normalized["current_cd_waiting_behind_harbor_110_repair_running"][
"blockers"
] == ["gitea_queue_current_cd_waiting_behind_harbor_110_repair_running"]
def test_harbor_recovery_receipt_surfaces_remote_control_timeout_queue_payload() -> None:
payload = validate_harbor_registry_controlled_recovery_receipt(
{
@@ -1139,6 +1193,39 @@ def _gitea_queue_cross_workflow_jobs() -> dict:
return payload
def _gitea_queue_cd_waiting_behind_stale_harbor_running() -> dict:
payload = _gitea_queue_cross_workflow_jobs()
payload["status"] = (
"blocked_current_cd_waiting_behind_stale_harbor_110_repair_readback"
)
payload["readback"].update(
{
"latest_visible_cd_run_id": "4243",
"latest_visible_cd_run_status": "Waiting",
"latest_visible_cd_run_waiting": True,
"latest_visible_harbor_110_repair_run_id": "4237",
"latest_visible_harbor_110_repair_run_status": "Running",
"latest_visible_harbor_110_repair_no_matching_runner_label": "",
"latest_visible_harbor_110_repair_waiting": False,
"latest_visible_harbor_110_repair_running": True,
"latest_visible_harbor_110_repair_blocked": True,
"harbor_110_repair_visible_running_jobs_api_stale": True,
"current_cd_waiting_behind_harbor_110_repair_running": True,
}
)
payload["rollups"].update(
{
"current_main_cd_run_waiting": True,
"harbor_110_repair_waiting": False,
"harbor_110_repair_running": True,
"harbor_110_repair_no_matching_runner_label": "",
"harbor_110_repair_visible_running_jobs_api_stale": True,
"current_cd_waiting_behind_harbor_110_repair_running": True,
}
)
return payload
def _gitea_queue_remote_control_timeout() -> dict:
payload = _gitea_queue_no_matching_runner()
payload["status"] = "blocked_harbor_110_remote_control_channel_unavailable"

View File

@@ -1,3 +1,16 @@
## 2026-07-01 — 14:15 Harbor receipt 納入 stale repair running blocker
**照主線修正的問題**
- Harbor controlled recovery receipt 先前能讀 `harbor_110_repair_jobs_payload_classifier` 與 current CD waiting但尚未把 `harbor_110_repair_visible_running_jobs_api_stale` / `current_cd_waiting_behind_harbor_110_repair_running` 轉成 receipt blocker。
- 本次把這兩個欄位接入 `gitea_actions_queue` readback、active blockers、control_path_readiness signal、rollups 與 normalized classifier fields。
- 目前 live queue 形狀 CD `Waiting` + Harbor repair `Running` + jobs API stale/cross-workflow 會被 receipt 層保留為 AI Agent / verifier / KM / RAG / PlayBook 可學習的 blocker。
**驗證**
- `DATABASE_URL=... PYTHONPATH=apps/api python3.11 -m pytest apps/api/tests/test_harbor_registry_controlled_recovery_receipt.py apps/api/tests/test_ai_agent_log_controlled_writeback_executor_readback_api.py apps/api/tests/test_awoooi_priority_work_order_readback_api.py apps/api/tests/test_ai_agent_autonomous_runtime_control.py ops/runner/test_read_public_gitea_actions_queue.py ops/runner/test_verify_awoooi_non110_cd_closure.py -q``89 passed`
- `ruff check``py_compile``git diff --check`:通過。
**邊界**:只改 metadata-only receipt normalizer / tests / LOGBOOK未使用 GitHub / `gh` / GitHub API未 workflow_dispatch未讀 secret / token / `.env` / raw sessions / SQLite / auth未讀 authorized_keys 內容或 `.runner` 內容;未執行 110 runtime apply。
## 2026-07-01 — 14:05 AI Agent normalizer 納入 stale Harbor repair readback
**照主線修正的問題**