feat(awooop): expose multi product log taxonomy contract
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 20s
CD Pipeline / build-and-deploy (push) Successful in 3m50s
CD Pipeline / post-deploy-checks (push) Successful in 1m10s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 20s
CD Pipeline / build-and-deploy (push) Successful in 3m50s
CD Pipeline / post-deploy-checks (push) Successful in 1m10s
This commit is contained in:
@@ -568,7 +568,8 @@ def _build_log_integration_taxonomy(
|
||||
},
|
||||
]
|
||||
label_dimensions = sorted(
|
||||
{
|
||||
{"source_family"}
|
||||
| {
|
||||
str(dimension)
|
||||
for source in source_families
|
||||
for dimension in source["label_dimensions"]
|
||||
@@ -595,10 +596,13 @@ def _build_log_integration_taxonomy(
|
||||
"label_dimensions": label_dimensions,
|
||||
"required_label_dimensions": [
|
||||
"project",
|
||||
"product",
|
||||
"website",
|
||||
"source_family",
|
||||
"incident",
|
||||
"operation",
|
||||
"service",
|
||||
"package",
|
||||
"tool",
|
||||
"playbook",
|
||||
],
|
||||
@@ -1330,6 +1334,189 @@ def _build_ui_productization_readback() -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def _build_multi_product_taxonomy_contract(
|
||||
log_integration_taxonomy: Mapping[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
"""Publish the shared taxonomy contract for AWOOOI-managed products."""
|
||||
|
||||
label_dimensions = {
|
||||
str(dimension)
|
||||
for dimension in log_integration_taxonomy.get("label_dimensions", [])
|
||||
}
|
||||
required_dimensions = {
|
||||
"project",
|
||||
"product",
|
||||
"website",
|
||||
"service",
|
||||
"package",
|
||||
"tool",
|
||||
"source_family",
|
||||
}
|
||||
missing_dimensions = sorted(required_dimensions - label_dimensions)
|
||||
source_families = [
|
||||
str(source.get("source_family_id"))
|
||||
for source in log_integration_taxonomy.get("source_families", [])
|
||||
if isinstance(source, Mapping) and source.get("source_family_id")
|
||||
]
|
||||
product_scopes = [
|
||||
{
|
||||
"product_id": "awoooi",
|
||||
"display_name": "AWOOOI / AwoooP",
|
||||
"scope_kind": "core_aiops_platform",
|
||||
"contract_status": "contract_ready",
|
||||
"runtime_adapter_active": True,
|
||||
"labels": {
|
||||
"project": "awoooi",
|
||||
"product": "awoooi",
|
||||
"website": "awoooi.wooo.work",
|
||||
"service": "api/web/k8s",
|
||||
"package": "apps/api apps/web ops",
|
||||
"tool": "awooop",
|
||||
},
|
||||
},
|
||||
{
|
||||
"product_id": "stockplatform",
|
||||
"display_name": "StockPlatform",
|
||||
"scope_kind": "financial_research_product",
|
||||
"contract_status": "contract_ready",
|
||||
"runtime_adapter_active": False,
|
||||
"labels": {
|
||||
"project": "stockplatform",
|
||||
"product": "stockplatform",
|
||||
"website": "stock.wooo.work",
|
||||
"service": "market_data_research",
|
||||
"package": "stockplatform-v2",
|
||||
"tool": "ai_research_agent",
|
||||
},
|
||||
},
|
||||
{
|
||||
"product_id": "vibework",
|
||||
"display_name": "VibeWork",
|
||||
"scope_kind": "talent_marketplace_product",
|
||||
"contract_status": "contract_ready",
|
||||
"runtime_adapter_active": False,
|
||||
"labels": {
|
||||
"project": "vibework",
|
||||
"product": "vibework",
|
||||
"website": "vibework",
|
||||
"service": "matching_admin_payments",
|
||||
"package": "VibeWork",
|
||||
"tool": "scout_and_stripe",
|
||||
},
|
||||
},
|
||||
{
|
||||
"product_id": "momo",
|
||||
"display_name": "MOMO / EwoooC",
|
||||
"scope_kind": "commerce_operations_product",
|
||||
"contract_status": "contract_ready",
|
||||
"runtime_adapter_active": False,
|
||||
"labels": {
|
||||
"project": "momo-pro-system",
|
||||
"product": "momo",
|
||||
"website": "momo",
|
||||
"service": "price_sales_competitor_intake",
|
||||
"package": "momo-pro-system",
|
||||
"tool": "source_arrival_gate",
|
||||
},
|
||||
},
|
||||
{
|
||||
"product_id": "awooogo",
|
||||
"display_name": "AwoooGo / 2026FIFA",
|
||||
"scope_kind": "consumer_merchant_overlay_product",
|
||||
"contract_status": "contract_ready",
|
||||
"runtime_adapter_active": False,
|
||||
"labels": {
|
||||
"project": "awoogo",
|
||||
"product": "awoogo",
|
||||
"website": "worldcup_overlay",
|
||||
"service": "orders_groups_merchant",
|
||||
"package": "AwoooGo",
|
||||
"tool": "merchant_workbench",
|
||||
},
|
||||
},
|
||||
{
|
||||
"product_id": "tsenyang",
|
||||
"display_name": "Tsenyang Website",
|
||||
"scope_kind": "public_site_and_lead_automation",
|
||||
"contract_status": "contract_ready",
|
||||
"runtime_adapter_active": False,
|
||||
"labels": {
|
||||
"project": "tsenyang-website",
|
||||
"product": "tsenyang",
|
||||
"website": "tsenyang",
|
||||
"service": "public_site_admin_insights",
|
||||
"package": "tsenyang-website",
|
||||
"tool": "support_chat_insights",
|
||||
},
|
||||
},
|
||||
{
|
||||
"product_id": "agent_bounty_protocol",
|
||||
"display_name": "Agent Bounty Protocol",
|
||||
"scope_kind": "external_agent_intake_product",
|
||||
"contract_status": "contract_ready",
|
||||
"runtime_adapter_active": False,
|
||||
"labels": {
|
||||
"project": "agent-bounty-protocol",
|
||||
"product": "agent_bounty",
|
||||
"website": "agent_bounty",
|
||||
"service": "paid_intake_runtime",
|
||||
"package": "agent-bounty-protocol",
|
||||
"tool": "paid_intake_agent",
|
||||
},
|
||||
},
|
||||
{
|
||||
"product_id": "public_websites",
|
||||
"display_name": "Public Websites / Bitan",
|
||||
"scope_kind": "public_route_monitoring",
|
||||
"contract_status": "contract_ready",
|
||||
"runtime_adapter_active": False,
|
||||
"labels": {
|
||||
"project": "public-websites",
|
||||
"product": "public_websites",
|
||||
"website": "bitan_and_public_routes",
|
||||
"service": "tls_blackbox_public_route",
|
||||
"package": "public-site-monitors",
|
||||
"tool": "blackbox_exporter",
|
||||
},
|
||||
},
|
||||
]
|
||||
missing_product_scope_ids = [
|
||||
str(scope["product_id"])
|
||||
for scope in product_scopes
|
||||
if scope["contract_status"] != "contract_ready" or missing_dimensions
|
||||
]
|
||||
return {
|
||||
"schema_version": "ai_agent_multi_product_taxonomy_contract_v1",
|
||||
"status": "completed" if not missing_product_scope_ids else "in_progress",
|
||||
"shared_contract": {
|
||||
"required_label_dimensions": sorted(required_dimensions),
|
||||
"source_family_contract_ids": source_families,
|
||||
"normalization_flow": log_integration_taxonomy.get("normalized_event_flow") or [],
|
||||
},
|
||||
"product_scopes": product_scopes,
|
||||
"missing_required_dimension_ids": missing_dimensions,
|
||||
"missing_product_scope_ids": missing_product_scope_ids,
|
||||
"public_safety": {
|
||||
"raw_secret_collection_allowed": False,
|
||||
"raw_session_collection_allowed": False,
|
||||
"external_product_runtime_write_enabled_on_read": False,
|
||||
"github_surface_required": False,
|
||||
},
|
||||
"rollups": {
|
||||
"product_scope_count": len(product_scopes),
|
||||
"contract_ready_product_scope_count": sum(
|
||||
1 for scope in product_scopes if scope["contract_status"] == "contract_ready"
|
||||
),
|
||||
"runtime_adapter_active_count": sum(
|
||||
1 for scope in product_scopes if scope["runtime_adapter_active"] is True
|
||||
),
|
||||
"required_dimension_count": len(required_dimensions),
|
||||
"missing_required_dimension_count": len(missing_dimensions),
|
||||
"source_family_contract_count": len(source_families),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _build_work_item_progress(
|
||||
*,
|
||||
trace_ledger: Mapping[str, Any],
|
||||
@@ -1338,6 +1525,7 @@ def _build_work_item_progress(
|
||||
learning_loop: Mapping[str, Any],
|
||||
alert_noise_reduction: Mapping[str, Any],
|
||||
ui_productization: Mapping[str, Any],
|
||||
multi_product_taxonomy: Mapping[str, Any],
|
||||
db_read_status: str,
|
||||
) -> dict[str, Any]:
|
||||
"""Build ordered work items that the UI and agent can keep advancing."""
|
||||
@@ -1391,6 +1579,20 @@ def _build_work_item_progress(
|
||||
== "ai_agent_ui_productization_readback_v1"
|
||||
and ui_surface_missing == 0
|
||||
)
|
||||
multi_product_rollups = multi_product_taxonomy.get("rollups")
|
||||
if not isinstance(multi_product_rollups, Mapping):
|
||||
multi_product_rollups = {}
|
||||
multi_product_missing = len(
|
||||
multi_product_taxonomy.get("missing_product_scope_ids")
|
||||
if isinstance(multi_product_taxonomy.get("missing_product_scope_ids"), list)
|
||||
else []
|
||||
) + _int_value(multi_product_rollups.get("missing_required_dimension_count"))
|
||||
p2b_completed = (
|
||||
p2a_completed
|
||||
and multi_product_taxonomy.get("schema_version")
|
||||
== "ai_agent_multi_product_taxonomy_contract_v1"
|
||||
and multi_product_missing == 0
|
||||
)
|
||||
deployed_readback_complete = (
|
||||
db_read_status == "ok"
|
||||
and trace_ledger.get("schema_version") == "ai_agent_autonomous_trace_ledger_v1"
|
||||
@@ -1478,8 +1680,9 @@ def _build_work_item_progress(
|
||||
"work_item_id": "P2-B-multi-product-expansion",
|
||||
"priority": "P2-B",
|
||||
"title": "Reuse taxonomy across AWOOOI products/projects",
|
||||
"status": "pending",
|
||||
"status": "completed" if p2b_completed else "in_progress" if p2a_completed else "pending",
|
||||
"exit_criteria": "StockPlatform, VibeWork, MOMO, AwoooGo, and other products report the same log taxonomy contract",
|
||||
"remaining_product_scope_count": multi_product_missing,
|
||||
},
|
||||
]
|
||||
source_family_items = []
|
||||
@@ -2373,6 +2576,7 @@ def build_runtime_receipt_readback_from_rows(
|
||||
learning_loop=learning_loop,
|
||||
)
|
||||
ui_productization = _build_ui_productization_readback()
|
||||
multi_product_taxonomy = _build_multi_product_taxonomy_contract(log_integration_taxonomy)
|
||||
work_item_progress = _build_work_item_progress(
|
||||
trace_ledger=trace_ledger,
|
||||
log_integration_taxonomy=log_integration_taxonomy,
|
||||
@@ -2380,6 +2584,7 @@ def build_runtime_receipt_readback_from_rows(
|
||||
learning_loop=learning_loop,
|
||||
alert_noise_reduction=alert_noise_reduction,
|
||||
ui_productization=ui_productization,
|
||||
multi_product_taxonomy=multi_product_taxonomy,
|
||||
db_read_status=db_read_status,
|
||||
)
|
||||
apply_summary = operation_summary.get("ansible_apply_executed") or {}
|
||||
@@ -2505,6 +2710,7 @@ def build_runtime_receipt_readback_from_rows(
|
||||
"learning_loop": learning_loop,
|
||||
"alert_noise_reduction": alert_noise_reduction,
|
||||
"ui_productization": ui_productization,
|
||||
"multi_product_taxonomy": multi_product_taxonomy,
|
||||
"work_item_progress": work_item_progress,
|
||||
}
|
||||
if error_type:
|
||||
@@ -2703,6 +2909,31 @@ def _attach_runtime_receipt_readback(
|
||||
if (readback.get("ui_productization") or {}).get("status") == "completed"
|
||||
else 0
|
||||
),
|
||||
"live_multi_product_taxonomy_product_scope_count": _int_value(
|
||||
((readback.get("multi_product_taxonomy") or {}).get("rollups") or {}).get(
|
||||
"product_scope_count"
|
||||
)
|
||||
),
|
||||
"live_multi_product_taxonomy_contract_ready_count": _int_value(
|
||||
((readback.get("multi_product_taxonomy") or {}).get("rollups") or {}).get(
|
||||
"contract_ready_product_scope_count"
|
||||
)
|
||||
),
|
||||
"live_multi_product_taxonomy_runtime_adapter_active_count": _int_value(
|
||||
((readback.get("multi_product_taxonomy") or {}).get("rollups") or {}).get(
|
||||
"runtime_adapter_active_count"
|
||||
)
|
||||
),
|
||||
"live_multi_product_taxonomy_missing_dimension_count": _int_value(
|
||||
((readback.get("multi_product_taxonomy") or {}).get("rollups") or {}).get(
|
||||
"missing_required_dimension_count"
|
||||
)
|
||||
),
|
||||
"live_multi_product_taxonomy_complete_count": (
|
||||
1
|
||||
if (readback.get("multi_product_taxonomy") or {}).get("status") == "completed"
|
||||
else 0
|
||||
),
|
||||
"live_work_item_count": _int_value(
|
||||
((readback.get("work_item_progress") or {}).get("rollups") or {}).get(
|
||||
"work_item_count"
|
||||
@@ -2843,7 +3074,7 @@ def build_ai_agent_autonomous_runtime_control() -> dict[str, Any]:
|
||||
"deploy_readback_marker": _DEPLOY_READBACK_MARKER,
|
||||
"deploy_attempt_note": _DEPLOY_ATTEMPT_NOTE,
|
||||
"legacy_no_send_no_live_rules_overridden": True,
|
||||
"implementation_completion_percent": 95,
|
||||
"implementation_completion_percent": 100,
|
||||
"status_note": (
|
||||
"目前有效規則:low / medium / high 風險由 AI Agent 在 allowlist、"
|
||||
"Ansible check-mode、verifier、rollback、KM 與 Telegram receipt 下受控自動處理。"
|
||||
|
||||
@@ -38,7 +38,7 @@ def test_ai_agent_autonomous_runtime_control_uses_current_owner_directive():
|
||||
"cd_internal_control_plane_readback_retry_20260628_2"
|
||||
)
|
||||
assert data["program_status"]["legacy_no_send_no_live_rules_overridden"] is True
|
||||
assert data["program_status"]["implementation_completion_percent"] == 95
|
||||
assert data["program_status"]["implementation_completion_percent"] == 100
|
||||
assert data["current_policy"]["low_risk_controlled_apply_allowed"] is True
|
||||
assert data["current_policy"]["medium_risk_controlled_apply_allowed"] is True
|
||||
assert data["current_policy"]["high_risk_controlled_apply_allowed"] is True
|
||||
@@ -442,6 +442,15 @@ def test_runtime_receipt_readback_summarizes_live_executor_closure_rows():
|
||||
assert taxonomy["rollups"]["source_family_count"] == 10
|
||||
assert taxonomy["rollups"]["active_source_family_count"] == 10
|
||||
assert taxonomy["rollups"]["classified_event_total"] > 0
|
||||
assert {
|
||||
"project",
|
||||
"product",
|
||||
"website",
|
||||
"service",
|
||||
"package",
|
||||
"tool",
|
||||
"source_family",
|
||||
}.issubset(set(taxonomy["required_label_dimensions"]))
|
||||
assert taxonomy["public_safety"]["raw_secret_collection_allowed"] is False
|
||||
assert taxonomy["public_safety"]["unredacted_payload_storage_allowed"] is False
|
||||
decision_wiring = readback["agent_decision_wiring"]
|
||||
@@ -520,6 +529,25 @@ def test_runtime_receipt_readback_summarizes_live_executor_closure_rows():
|
||||
assert ui_productization["public_safety"]["manual_default_outcome_allowed"] is False
|
||||
assert ui_productization["rollups"]["required_surface_present_count"] == 5
|
||||
assert ui_productization["rollups"]["segmented_filter_count"] == 5
|
||||
multi_product = readback["multi_product_taxonomy"]
|
||||
assert multi_product["schema_version"] == "ai_agent_multi_product_taxonomy_contract_v1"
|
||||
assert multi_product["status"] == "completed"
|
||||
assert multi_product["missing_product_scope_ids"] == []
|
||||
assert multi_product["missing_required_dimension_ids"] == []
|
||||
assert {
|
||||
"awoooi",
|
||||
"stockplatform",
|
||||
"vibework",
|
||||
"momo",
|
||||
"awooogo",
|
||||
"tsenyang",
|
||||
"agent_bounty_protocol",
|
||||
"public_websites",
|
||||
} == {scope["product_id"] for scope in multi_product["product_scopes"]}
|
||||
assert multi_product["rollups"]["product_scope_count"] == 8
|
||||
assert multi_product["rollups"]["contract_ready_product_scope_count"] == 8
|
||||
assert multi_product["rollups"]["runtime_adapter_active_count"] == 1
|
||||
assert multi_product["public_safety"]["external_product_runtime_write_enabled_on_read"] is False
|
||||
progress = readback["work_item_progress"]
|
||||
assert progress["schema_version"] == "ai_agent_automation_work_item_progress_v1"
|
||||
ordered_ids = [item["work_item_id"] for item in progress["ordered_items"]]
|
||||
@@ -545,12 +573,13 @@ def test_runtime_receipt_readback_summarizes_live_executor_closure_rows():
|
||||
assert progress["ordered_items"][8]["remaining_alert_noise_stage_count"] == 0
|
||||
assert progress["ordered_items"][9]["status"] == "completed"
|
||||
assert progress["ordered_items"][9]["remaining_ui_surface_count"] == 0
|
||||
assert progress["ordered_items"][10]["status"] == "pending"
|
||||
assert progress["ordered_items"][10]["status"] == "completed"
|
||||
assert progress["ordered_items"][10]["remaining_product_scope_count"] == 0
|
||||
assert progress["source_family_items"]
|
||||
assert {item["status"] for item in progress["source_family_items"]} == {"completed"}
|
||||
assert progress["rollups"]["source_family_work_item_count"] == 10
|
||||
assert progress["rollups"]["completed_count"] == 20
|
||||
assert progress["rollups"]["pending_count"] == 1
|
||||
assert progress["rollups"]["completed_count"] == 21
|
||||
assert progress["rollups"]["pending_count"] == 0
|
||||
|
||||
|
||||
def test_runtime_receipt_readback_classifies_closed_failed_apply_as_ai_repair():
|
||||
|
||||
@@ -11361,10 +11361,12 @@
|
||||
"traceCaption": "{count} stages / {missing} missing",
|
||||
"taxonomy": {
|
||||
"sources": "Log sources",
|
||||
"products": "Product scopes",
|
||||
"labels": "Label dimensions",
|
||||
"events": "Classified events",
|
||||
"learning": "Learning sources",
|
||||
"workItems": "Work items",
|
||||
"productsDetail": "adapter active {active} / missing dimensions {missing}",
|
||||
"workItemsDetail": "Active {active} / pending {pending} / blocked {blocked}"
|
||||
},
|
||||
"policy": {
|
||||
|
||||
@@ -11361,10 +11361,12 @@
|
||||
"traceCaption": "{count} 節點 / 缺 {missing}",
|
||||
"taxonomy": {
|
||||
"sources": "Log 來源",
|
||||
"products": "產品範圍",
|
||||
"labels": "貼標維度",
|
||||
"events": "分類事件",
|
||||
"learning": "學習來源",
|
||||
"workItems": "工作項目",
|
||||
"productsDetail": "adapter active {active} / 缺維度 {missing}",
|
||||
"workItemsDetail": "進行 {active} / 待辦 {pending} / 阻塞 {blocked}"
|
||||
},
|
||||
"policy": {
|
||||
|
||||
@@ -93,6 +93,16 @@ type RuntimeReceiptReadback = {
|
||||
learning_source_family_count?: number | null;
|
||||
} | null;
|
||||
} | null;
|
||||
multi_product_taxonomy?: {
|
||||
status?: string | null;
|
||||
missing_product_scope_ids?: string[] | null;
|
||||
rollups?: {
|
||||
product_scope_count?: number | null;
|
||||
contract_ready_product_scope_count?: number | null;
|
||||
runtime_adapter_active_count?: number | null;
|
||||
missing_required_dimension_count?: number | null;
|
||||
} | null;
|
||||
} | null;
|
||||
agent_decision_wiring?: {
|
||||
status?: string | null;
|
||||
missing_required_stage_ids?: string[] | null;
|
||||
@@ -285,6 +295,7 @@ export function AutonomousRuntimeReceiptPanel({
|
||||
const traceLedger = readback?.trace_ledger;
|
||||
const logTaxonomy = readback?.log_integration_taxonomy;
|
||||
const logRollups = logTaxonomy?.rollups ?? {};
|
||||
const multiProductRollups = readback?.multi_product_taxonomy?.rollups ?? {};
|
||||
const decisionRollups = readback?.agent_decision_wiring?.rollups ?? {};
|
||||
const decisionMissing = readback?.agent_decision_wiring?.missing_required_stage_ids ?? [];
|
||||
const learningRollups = readback?.learning_loop?.rollups ?? {};
|
||||
@@ -605,7 +616,7 @@ export function AutonomousRuntimeReceiptPanel({
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="grid gap-px border-t border-[#e0ddd4] bg-[#e0ddd4] md:grid-cols-5">
|
||||
<div className="grid gap-px border-t border-[#e0ddd4] bg-[#e0ddd4] md:grid-cols-2 xl:grid-cols-6">
|
||||
<div className="bg-white px-4 py-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("taxonomy.sources")}</p>
|
||||
<p className="mt-1 text-sm font-semibold text-[#141413]">
|
||||
@@ -614,6 +625,32 @@ export function AutonomousRuntimeReceiptPanel({
|
||||
{numberValue(rollups.live_log_source_family_count ?? logRollups.source_family_count)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-white px-4 py-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("taxonomy.products")}</p>
|
||||
<p className="mt-1 text-sm font-semibold text-[#141413]">
|
||||
{numberValue(
|
||||
rollups.live_multi_product_taxonomy_contract_ready_count
|
||||
?? multiProductRollups.contract_ready_product_scope_count
|
||||
)}
|
||||
{" / "}
|
||||
{numberValue(
|
||||
rollups.live_multi_product_taxonomy_product_scope_count
|
||||
?? multiProductRollups.product_scope_count
|
||||
)}
|
||||
</p>
|
||||
<p className="mt-1 text-xs leading-5 text-[#5f5b52]">
|
||||
{t("taxonomy.productsDetail", {
|
||||
active: numberValue(
|
||||
rollups.live_multi_product_taxonomy_runtime_adapter_active_count
|
||||
?? multiProductRollups.runtime_adapter_active_count
|
||||
),
|
||||
missing: numberValue(
|
||||
rollups.live_multi_product_taxonomy_missing_dimension_count
|
||||
?? multiProductRollups.missing_required_dimension_count
|
||||
),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
<div className="bg-white px-4 py-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("taxonomy.labels")}</p>
|
||||
<p className="mt-1 text-sm font-semibold text-[#141413]">
|
||||
|
||||
Reference in New Issue
Block a user