Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5450e05205 | ||
|
|
18b20942fd | ||
|
|
1ca8fe3228 | ||
|
|
c1ed0ec449 | ||
|
|
06e45d089f | ||
|
|
3af3cca6ad | ||
|
|
809449a392 | ||
|
|
715107df1f | ||
|
|
09e8987355 | ||
|
|
302000e27d | ||
|
|
17cd30ec6d | ||
|
|
21a8a22ca8 | ||
|
|
cf7c69de18 | ||
|
|
643c456351 | ||
|
|
ce88fccae9 | ||
|
|
463850eb1b | ||
|
|
04c3aca7da | ||
|
|
75a2e7cd1f | ||
|
|
b0655da8b3 | ||
|
|
7fdcc52b56 | ||
|
|
f0a33f3adc | ||
|
|
33b7027a55 | ||
|
|
e2a546f077 | ||
|
|
e990780c88 |
@@ -0,0 +1,45 @@
|
|||||||
|
portal_aios_acceptance:
|
||||||
|
version: V1R4C3
|
||||||
|
status: pending_deployment_validation
|
||||||
|
|
||||||
|
login:
|
||||||
|
- casdoor_auth_success
|
||||||
|
- portal_session_created
|
||||||
|
- portal_me_loaded
|
||||||
|
|
||||||
|
roles:
|
||||||
|
user:
|
||||||
|
checks:
|
||||||
|
- task_center_visible
|
||||||
|
- owner_modules_hidden
|
||||||
|
group_admin:
|
||||||
|
checks:
|
||||||
|
- task_center_visible
|
||||||
|
- operations_visible
|
||||||
|
- system_health_visible
|
||||||
|
- search_visible
|
||||||
|
- agents_visible
|
||||||
|
owner:
|
||||||
|
checks:
|
||||||
|
- all_aios_modules_visible
|
||||||
|
- governance_visible
|
||||||
|
- architecture_visible
|
||||||
|
- owner_gate_visible
|
||||||
|
|
||||||
|
api:
|
||||||
|
- manifest_returns_modules
|
||||||
|
- dashboard_returns_navigation
|
||||||
|
- rbac_filters_modules
|
||||||
|
|
||||||
|
ui:
|
||||||
|
- menu_rendered
|
||||||
|
- cards_rendered
|
||||||
|
- quick_actions_rendered
|
||||||
|
- forbidden_routes_hidden
|
||||||
|
|
||||||
|
success_signals:
|
||||||
|
- AIOS_PORTAL_ROUTE_READY
|
||||||
|
- AIOS_WEB_UI_MODULES_VISIBLE
|
||||||
|
- AIOS_RBAC_VISIBILITY_READY
|
||||||
|
- AIOS_PORTAL_AUTH_INTEGRATION_READY
|
||||||
|
- AIOS_PORTAL_USER_EXPERIENCE_READY
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
"""Autonomous goal completion supervisor.
|
||||||
|
|
||||||
|
Coordinates open goals until completion without requiring the user to keep asking
|
||||||
|
for progress. It breaks goals into tasks, routes work to the correct agent,
|
||||||
|
tracks blockers, retries safe failures, requests approvals only when policy
|
||||||
|
requires, and sends completion notifications.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import asdict, dataclass, field
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from typing import Any
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
|
||||||
|
def utc_now() -> str:
|
||||||
|
return datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class CompletionGoal:
|
||||||
|
goal_id: str
|
||||||
|
title: str
|
||||||
|
owner: str
|
||||||
|
status: str = "open"
|
||||||
|
priority: str = "P1"
|
||||||
|
target_signals: list[str] = field(default_factory=list)
|
||||||
|
tasks: list[dict[str, Any]] = field(default_factory=list)
|
||||||
|
created_at: str = field(default_factory=utc_now)
|
||||||
|
updated_at: str = field(default_factory=utc_now)
|
||||||
|
|
||||||
|
def to_dict(self) -> dict[str, Any]:
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class CompletionDecision:
|
||||||
|
status: str
|
||||||
|
action: str
|
||||||
|
assigned_to: str
|
||||||
|
reason: str
|
||||||
|
requires_user: bool = False
|
||||||
|
requires_owner_gate: bool = False
|
||||||
|
notification_required: bool = True
|
||||||
|
|
||||||
|
def to_dict(self) -> dict[str, Any]:
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
|
||||||
|
class AutonomousGoalCompletionSupervisor:
|
||||||
|
"""Completes goals through AIOS autonomous loops."""
|
||||||
|
|
||||||
|
HARD_OWNER_GATE_REASONS = {
|
||||||
|
"secret_required",
|
||||||
|
"paid_provider_required",
|
||||||
|
"production_baseline_promotion",
|
||||||
|
"large_architecture_change",
|
||||||
|
"cross_tenant_data_change",
|
||||||
|
"private_data_source_enablement",
|
||||||
|
}
|
||||||
|
|
||||||
|
def create_goal(self, title: str, owner: str = "owner", priority: str = "P1", target_signals: list[str] | None = None) -> CompletionGoal:
|
||||||
|
return CompletionGoal(
|
||||||
|
goal_id=f"goal_{uuid4().hex[:12]}",
|
||||||
|
title=title,
|
||||||
|
owner=owner,
|
||||||
|
priority=priority,
|
||||||
|
target_signals=target_signals or [],
|
||||||
|
)
|
||||||
|
|
||||||
|
def decide_next_action(self, goal: dict[str, Any], health: dict[str, Any] | None = None) -> CompletionDecision:
|
||||||
|
health = health or {}
|
||||||
|
blockers = health.get("blockers", []) if isinstance(health.get("blockers"), list) else []
|
||||||
|
owner_reason = self._owner_gate_reason(goal, blockers)
|
||||||
|
if owner_reason:
|
||||||
|
return CompletionDecision(
|
||||||
|
status="waiting_owner_gate",
|
||||||
|
action="request_owner_approval",
|
||||||
|
assigned_to="owner-gate",
|
||||||
|
reason=owner_reason,
|
||||||
|
requires_user=True,
|
||||||
|
requires_owner_gate=True,
|
||||||
|
)
|
||||||
|
if self._has_p0_blocker(blockers):
|
||||||
|
return CompletionDecision(
|
||||||
|
status="self_healing",
|
||||||
|
action="run_self_healing_task",
|
||||||
|
assigned_to="openclaw",
|
||||||
|
reason="P0 blocker detected; self-healing before continuing",
|
||||||
|
)
|
||||||
|
if not goal.get("tasks"):
|
||||||
|
return CompletionDecision(
|
||||||
|
status="planning",
|
||||||
|
action="create_task_plan",
|
||||||
|
assigned_to="autonomous-controller",
|
||||||
|
reason="goal has no tasks yet",
|
||||||
|
)
|
||||||
|
unfinished = [task for task in goal.get("tasks", []) if task.get("status") not in {"completed", "closed"}]
|
||||||
|
if unfinished:
|
||||||
|
return CompletionDecision(
|
||||||
|
status="executing",
|
||||||
|
action="continue_unfinished_tasks",
|
||||||
|
assigned_to=unfinished[0].get("executor", "openclaw"),
|
||||||
|
reason="unfinished tasks remain",
|
||||||
|
)
|
||||||
|
return CompletionDecision(
|
||||||
|
status="completed",
|
||||||
|
action="send_completion_notification",
|
||||||
|
assigned_to="notification-agent",
|
||||||
|
reason="all tasks completed",
|
||||||
|
)
|
||||||
|
|
||||||
|
def completion_summary(self, goal: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
tasks = goal.get("tasks", [])
|
||||||
|
completed = [task for task in tasks if task.get("status") in {"completed", "closed"}]
|
||||||
|
blocked = [task for task in tasks if task.get("status") in {"blocked", "waiting_approval"}]
|
||||||
|
return {
|
||||||
|
"goal_id": goal.get("goal_id"),
|
||||||
|
"title": goal.get("title"),
|
||||||
|
"status": "completed" if tasks and len(completed) == len(tasks) else goal.get("status", "open"),
|
||||||
|
"total_tasks": len(tasks),
|
||||||
|
"completed_tasks": len(completed),
|
||||||
|
"blocked_tasks": len(blocked),
|
||||||
|
"completion_rate": round(len(completed) / len(tasks) * 100, 2) if tasks else 0,
|
||||||
|
"updated_at": utc_now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def _owner_gate_reason(self, goal: dict[str, Any], blockers: list[dict[str, Any]]) -> str | None:
|
||||||
|
for reason in goal.get("owner_gate_reasons", []):
|
||||||
|
if reason in self.HARD_OWNER_GATE_REASONS:
|
||||||
|
return reason
|
||||||
|
for blocker in blockers:
|
||||||
|
reason = blocker.get("owner_gate_reason")
|
||||||
|
if reason in self.HARD_OWNER_GATE_REASONS:
|
||||||
|
return reason
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _has_p0_blocker(self, blockers: list[dict[str, Any]]) -> bool:
|
||||||
|
return any(blocker.get("severity") in {"p0", "critical"} for blocker in blockers)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["CompletionGoal", "CompletionDecision", "AutonomousGoalCompletionSupervisor"]
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
"""Portal AIOS dashboard aggregation API."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
from fastapi import APIRouter
|
||||||
|
except Exception:
|
||||||
|
APIRouter = None
|
||||||
|
|
||||||
|
from .portal_aios_module_registry import PortalAIOSModuleRegistry
|
||||||
|
from .portal_aios_navigation import PortalAIOSNavigationBuilder
|
||||||
|
|
||||||
|
|
||||||
|
registry = PortalAIOSModuleRegistry()
|
||||||
|
navigation = PortalAIOSNavigationBuilder(registry)
|
||||||
|
|
||||||
|
|
||||||
|
def get_portal_aios_dashboard(role: str = "user"):
|
||||||
|
return {
|
||||||
|
"entry": "/portal/me/",
|
||||||
|
"role": role,
|
||||||
|
"modules": registry.route_manifest(role),
|
||||||
|
"navigation": navigation.build(role),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_router():
|
||||||
|
if APIRouter is None:
|
||||||
|
return None
|
||||||
|
router = APIRouter(prefix="/portal/me/aios", tags=["portal-aios-dashboard"])
|
||||||
|
|
||||||
|
@router.get("/dashboard", operation_id="getPortalAIOSDashboard")
|
||||||
|
def dashboard(role: str = "user"):
|
||||||
|
return get_portal_aios_dashboard(role)
|
||||||
|
|
||||||
|
return router
|
||||||
|
|
||||||
|
|
||||||
|
router = build_router()
|
||||||
|
|
||||||
|
__all__ = ["router", "get_portal_aios_dashboard"]
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
"""Portal AIOS module manifest API."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
from fastapi import APIRouter
|
||||||
|
except Exception:
|
||||||
|
APIRouter = None
|
||||||
|
|
||||||
|
from .portal_aios_module_registry import PortalAIOSModuleRegistry
|
||||||
|
|
||||||
|
|
||||||
|
registry = PortalAIOSModuleRegistry()
|
||||||
|
|
||||||
|
|
||||||
|
def get_portal_manifest(role: str = "owner"):
|
||||||
|
return registry.route_manifest(role)
|
||||||
|
|
||||||
|
|
||||||
|
def build_router():
|
||||||
|
if APIRouter is None:
|
||||||
|
return None
|
||||||
|
router = APIRouter(prefix="/portal/me/aios", tags=["portal-aios"])
|
||||||
|
|
||||||
|
@router.get("/manifest", operation_id="getPortalAIOSManifest")
|
||||||
|
def manifest(role: str = "owner"):
|
||||||
|
return get_portal_manifest(role)
|
||||||
|
|
||||||
|
return router
|
||||||
|
|
||||||
|
|
||||||
|
router = build_router()
|
||||||
|
|
||||||
|
__all__ = ["router", "get_portal_manifest"]
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
"""AIOS Portal module registry.
|
||||||
|
|
||||||
|
Defines the product-level modules that should be visible under
|
||||||
|
https://user.cnnetmkt.top/portal/me/ after authentication. This bridges backend
|
||||||
|
AIOS capabilities into portal routes, menu entries and RBAC visibility.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import asdict, dataclass, field
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
ROLE_ORDER = {
|
||||||
|
"user": 1,
|
||||||
|
"group_admin": 5,
|
||||||
|
"owner": 9,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class PortalAIOSModule:
|
||||||
|
key: str
|
||||||
|
title: str
|
||||||
|
route: str
|
||||||
|
api_routes: list[str]
|
||||||
|
min_role: str = "user"
|
||||||
|
description: str = ""
|
||||||
|
children: list["PortalAIOSModule"] = field(default_factory=list)
|
||||||
|
|
||||||
|
def visible_to(self, role: str) -> bool:
|
||||||
|
return ROLE_ORDER.get(role, 0) >= ROLE_ORDER.get(self.min_role, 0)
|
||||||
|
|
||||||
|
def to_dict(self, role: str = "owner") -> dict[str, Any] | None:
|
||||||
|
if not self.visible_to(role):
|
||||||
|
return None
|
||||||
|
return {
|
||||||
|
"key": self.key,
|
||||||
|
"title": self.title,
|
||||||
|
"route": self.route,
|
||||||
|
"api_routes": self.api_routes,
|
||||||
|
"min_role": self.min_role,
|
||||||
|
"description": self.description,
|
||||||
|
"children": [child for child in [c.to_dict(role) for c in self.children] if child],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PortalAIOSModuleRegistry:
|
||||||
|
def modules(self) -> list[PortalAIOSModule]:
|
||||||
|
return [
|
||||||
|
PortalAIOSModule(
|
||||||
|
key="tasks",
|
||||||
|
title="任务中心",
|
||||||
|
route="/admin/ai-evolution/tasks",
|
||||||
|
api_routes=["/admin/ai-evolution/tasks/overview"],
|
||||||
|
min_role="user",
|
||||||
|
description="查看任务来源、执行者、进度、阻塞、审批和通知状态。",
|
||||||
|
),
|
||||||
|
PortalAIOSModule(
|
||||||
|
key="operations",
|
||||||
|
title="自治运营驾驶舱",
|
||||||
|
route="/admin/ai-evolution/operations",
|
||||||
|
api_routes=["/admin/ai-evolution/operations/overview"],
|
||||||
|
min_role="group_admin",
|
||||||
|
description="统一查看任务、健康、阻塞、自愈和搜索运行状态。",
|
||||||
|
),
|
||||||
|
PortalAIOSModule(
|
||||||
|
key="system_health",
|
||||||
|
title="系统健康",
|
||||||
|
route="/admin/ai-evolution/system-health",
|
||||||
|
api_routes=["/admin/ai-evolution/system-health/overview"],
|
||||||
|
min_role="group_admin",
|
||||||
|
description="查看 P0 问题、重复阻塞、自愈记录和系统健康。",
|
||||||
|
),
|
||||||
|
PortalAIOSModule(
|
||||||
|
key="search",
|
||||||
|
title="搜索治理",
|
||||||
|
route="/admin/ai-evolution/search",
|
||||||
|
api_routes=[
|
||||||
|
"/admin/ai-evolution/search/status",
|
||||||
|
"/admin/ai-evolution/search/overview",
|
||||||
|
"/admin/ai-evolution/search/query",
|
||||||
|
],
|
||||||
|
min_role="group_admin",
|
||||||
|
description="查看联网搜索 Provider、Profile、Fallback、证据和审计。",
|
||||||
|
),
|
||||||
|
PortalAIOSModule(
|
||||||
|
key="agents",
|
||||||
|
title="Agent 观察",
|
||||||
|
route="/admin/ai-evolution/agents",
|
||||||
|
api_routes=["/admin/ai-evolution/agents/overview"],
|
||||||
|
min_role="group_admin",
|
||||||
|
description="查看 OpenClaw、Hermes、Codex 等 Agent 执行链路。",
|
||||||
|
),
|
||||||
|
PortalAIOSModule(
|
||||||
|
key="architecture_changes",
|
||||||
|
title="架构变更",
|
||||||
|
route="/admin/ai-evolution/architecture-changes",
|
||||||
|
api_routes=["/admin/ai-evolution/architecture-changes/overview"],
|
||||||
|
min_role="owner",
|
||||||
|
description="查看架构变化分类、Owner Gate 和 C/R/V 建议。",
|
||||||
|
),
|
||||||
|
PortalAIOSModule(
|
||||||
|
key="architect",
|
||||||
|
title="架构治理",
|
||||||
|
route="/admin/ai-evolution/architect",
|
||||||
|
api_routes=["/admin/ai-evolution/architect/overview"],
|
||||||
|
min_role="owner",
|
||||||
|
description="查看需求变化、架构审查、文档刷新和版本建议。",
|
||||||
|
children=[
|
||||||
|
PortalAIOSModule(
|
||||||
|
key="architect_research",
|
||||||
|
title="技术情报",
|
||||||
|
route="/admin/ai-evolution/architect/research",
|
||||||
|
api_routes=["/admin/ai-evolution/architect/research/timeline"],
|
||||||
|
min_role="owner",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
PortalAIOSModule(
|
||||||
|
key="governance",
|
||||||
|
title="变更治理",
|
||||||
|
route="/admin/ai-evolution/governance",
|
||||||
|
api_routes=[
|
||||||
|
"/admin/ai-evolution/governance/overview",
|
||||||
|
"/admin/ai-evolution/governance/timeline",
|
||||||
|
],
|
||||||
|
min_role="owner",
|
||||||
|
description="查看测试 Gate、用户验收、架构审查和发布决策链。",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
def visible_modules(self, role: str) -> list[dict[str, Any]]:
|
||||||
|
return [module for module in [m.to_dict(role) for m in self.modules()] if module]
|
||||||
|
|
||||||
|
def route_manifest(self, role: str = "owner") -> dict[str, Any]:
|
||||||
|
modules = self.visible_modules(role)
|
||||||
|
return {
|
||||||
|
"portal_entry": "/portal/me/",
|
||||||
|
"role": role,
|
||||||
|
"modules": modules,
|
||||||
|
"routes": [module["route"] for module in modules],
|
||||||
|
"api_routes": [api for module in modules for api in module.get("api_routes", [])],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["PortalAIOSModule", "PortalAIOSModuleRegistry"]
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
"""Portal navigation builder for AIOS modules.
|
||||||
|
|
||||||
|
Builds role-aware navigation entries consumed by the portal frontend under
|
||||||
|
/portal/me/. The frontend can render these entries as cards, sidebar items or
|
||||||
|
quick actions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import asdict, dataclass
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from .portal_aios_module_registry import PortalAIOSModuleRegistry
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class PortalNavigationItem:
|
||||||
|
key: str
|
||||||
|
label: str
|
||||||
|
href: str
|
||||||
|
icon: str
|
||||||
|
description: str
|
||||||
|
badge: str | None = None
|
||||||
|
order: int = 100
|
||||||
|
|
||||||
|
def to_dict(self) -> dict[str, Any]:
|
||||||
|
return asdict(self)
|
||||||
|
|
||||||
|
|
||||||
|
class PortalAIOSNavigationBuilder:
|
||||||
|
ICONS = {
|
||||||
|
"tasks": "list-checks",
|
||||||
|
"operations": "activity",
|
||||||
|
"system_health": "heart-pulse",
|
||||||
|
"search": "search",
|
||||||
|
"agents": "bot",
|
||||||
|
"architecture_changes": "git-branch",
|
||||||
|
"architect": "network",
|
||||||
|
"governance": "shield-check",
|
||||||
|
}
|
||||||
|
|
||||||
|
ORDER = {
|
||||||
|
"tasks": 10,
|
||||||
|
"operations": 20,
|
||||||
|
"system_health": 30,
|
||||||
|
"search": 40,
|
||||||
|
"agents": 50,
|
||||||
|
"architecture_changes": 60,
|
||||||
|
"architect": 70,
|
||||||
|
"governance": 80,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, registry: PortalAIOSModuleRegistry | None = None):
|
||||||
|
self.registry = registry or PortalAIOSModuleRegistry()
|
||||||
|
|
||||||
|
def build(self, role: str = "user") -> dict[str, Any]:
|
||||||
|
manifest = self.registry.route_manifest(role)
|
||||||
|
items = [self._item(module) for module in manifest["modules"]]
|
||||||
|
items.sort(key=lambda item: item.order)
|
||||||
|
return {
|
||||||
|
"portal_entry": "/portal/me/",
|
||||||
|
"role": role,
|
||||||
|
"section": "AIOS",
|
||||||
|
"items": [item.to_dict() for item in items],
|
||||||
|
"quick_actions": self.quick_actions(role),
|
||||||
|
}
|
||||||
|
|
||||||
|
def quick_actions(self, role: str) -> list[dict[str, Any]]:
|
||||||
|
actions = [
|
||||||
|
{"key": "open_tasks", "label": "查看任务", "href": "/admin/ai-evolution/tasks"},
|
||||||
|
]
|
||||||
|
if role in {"group_admin", "owner"}:
|
||||||
|
actions.append({"key": "open_operations", "label": "自治运营", "href": "/admin/ai-evolution/operations"})
|
||||||
|
actions.append({"key": "open_health", "label": "系统健康", "href": "/admin/ai-evolution/system-health"})
|
||||||
|
if role == "owner":
|
||||||
|
actions.append({"key": "open_owner_gate", "label": "审批治理", "href": "/admin/ai-evolution/governance"})
|
||||||
|
return actions
|
||||||
|
|
||||||
|
def _item(self, module: dict[str, Any]) -> PortalNavigationItem:
|
||||||
|
key = module["key"]
|
||||||
|
return PortalNavigationItem(
|
||||||
|
key=key,
|
||||||
|
label=module["title"],
|
||||||
|
href=module["route"],
|
||||||
|
icon=self.ICONS.get(key, "box"),
|
||||||
|
description=module.get("description", ""),
|
||||||
|
badge=self._badge(key),
|
||||||
|
order=self.ORDER.get(key, 100),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _badge(self, key: str) -> str | None:
|
||||||
|
if key in {"tasks", "system_health"}:
|
||||||
|
return "live"
|
||||||
|
if key in {"architecture_changes", "governance"}:
|
||||||
|
return "owner"
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["PortalNavigationItem", "PortalAIOSNavigationBuilder"]
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
"""Portal integration health API."""
|
||||||
|
|
||||||
|
try:
|
||||||
|
from fastapi import APIRouter
|
||||||
|
except Exception:
|
||||||
|
APIRouter = None
|
||||||
|
|
||||||
|
from .portal_integration_health import PortalIntegrationHealth
|
||||||
|
|
||||||
|
|
||||||
|
health = PortalIntegrationHealth()
|
||||||
|
|
||||||
|
|
||||||
|
def get_portal_integration_health():
|
||||||
|
return health.check()
|
||||||
|
|
||||||
|
|
||||||
|
def build_router():
|
||||||
|
if APIRouter is None:
|
||||||
|
return None
|
||||||
|
router = APIRouter(prefix="/portal/me/aios", tags=["portal-health"])
|
||||||
|
|
||||||
|
@router.get("/health", operation_id="getPortalIntegrationHealth")
|
||||||
|
def health_check():
|
||||||
|
return get_portal_integration_health()
|
||||||
|
|
||||||
|
return router
|
||||||
|
|
||||||
|
|
||||||
|
router = build_router()
|
||||||
|
|
||||||
|
__all__ = ["router", "get_portal_integration_health"]
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
"""Portal integration health checks."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
def utc_now() -> str:
|
||||||
|
return datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
|
|
||||||
|
class PortalIntegrationHealth:
|
||||||
|
def check(self, manifest: dict[str, Any] | None = None, frontend: dict[str, Any] | None = None) -> dict[str, Any]:
|
||||||
|
manifest = manifest or {}
|
||||||
|
frontend = frontend or {}
|
||||||
|
routes = manifest.get("routes", [])
|
||||||
|
return {
|
||||||
|
"module": "portal-integration-health",
|
||||||
|
"status": "healthy" if routes else "attention_required",
|
||||||
|
"checked_at": utc_now(),
|
||||||
|
"checks": {
|
||||||
|
"manifest_available": bool(manifest),
|
||||||
|
"routes_available": len(routes) > 0,
|
||||||
|
"frontend_registered": frontend.get("registered", False),
|
||||||
|
"rbac_enabled": True,
|
||||||
|
},
|
||||||
|
"missing": self._missing(manifest, frontend),
|
||||||
|
}
|
||||||
|
|
||||||
|
def _missing(self, manifest: dict[str, Any], frontend: dict[str, Any]) -> list[str]:
|
||||||
|
missing = []
|
||||||
|
if not manifest:
|
||||||
|
missing.append("portal_manifest")
|
||||||
|
if not frontend.get("registered", False):
|
||||||
|
missing.append("frontend_navigation_registration")
|
||||||
|
return missing
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["PortalIntegrationHealth"]
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
"""Portal integration adapter for System Health Center.
|
||||||
|
|
||||||
|
Adds portal visibility state to the AIOS health model so missing user-facing
|
||||||
|
routes are treated as operational issues, not merely frontend tasks.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from .portal_aios_module_registry import PortalAIOSModuleRegistry
|
||||||
|
from .portal_integration_health import PortalIntegrationHealth
|
||||||
|
|
||||||
|
|
||||||
|
class PortalSystemHealthAdapter:
|
||||||
|
def __init__(self):
|
||||||
|
self.registry = PortalAIOSModuleRegistry()
|
||||||
|
self.health = PortalIntegrationHealth()
|
||||||
|
|
||||||
|
def build(self, role: str = "owner", frontend: dict[str, Any] | None = None) -> dict[str, Any]:
|
||||||
|
manifest = self.registry.route_manifest(role)
|
||||||
|
health = self.health.check(manifest, frontend or {})
|
||||||
|
return {
|
||||||
|
"component": "portal-aios-integration",
|
||||||
|
"role": role,
|
||||||
|
"status": health["status"],
|
||||||
|
"manifest_routes": manifest.get("routes", []),
|
||||||
|
"api_routes": manifest.get("api_routes", []),
|
||||||
|
"missing": health.get("missing", []),
|
||||||
|
"checks": health.get("checks", {}),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["PortalSystemHealthAdapter"]
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
autonomous_goal_completion:
|
||||||
|
enabled: true
|
||||||
|
purpose: complete_goals_without_manual_progress_queries
|
||||||
|
|
||||||
|
autonomous_actions:
|
||||||
|
- create_task_plan
|
||||||
|
- assign_agent
|
||||||
|
- monitor_progress
|
||||||
|
- retry_safe_failure
|
||||||
|
- trigger_self_healing
|
||||||
|
- update_task_center
|
||||||
|
- send_completion_notification
|
||||||
|
|
||||||
|
require_owner_gate:
|
||||||
|
- secret_required
|
||||||
|
- paid_provider_required
|
||||||
|
- production_baseline_promotion
|
||||||
|
- large_architecture_change
|
||||||
|
- cross_tenant_data_change
|
||||||
|
- private_data_source_enablement
|
||||||
|
|
||||||
|
integrations:
|
||||||
|
task_center: true
|
||||||
|
system_health: true
|
||||||
|
blocker_memory: true
|
||||||
|
architect_agent: true
|
||||||
|
notification_agent: true
|
||||||
|
|
||||||
|
reporting:
|
||||||
|
notify_on:
|
||||||
|
- blocked
|
||||||
|
- owner_gate_required
|
||||||
|
- completed
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
portal_aios_frontend_contract:
|
||||||
|
enabled: true
|
||||||
|
entry: /portal/me/
|
||||||
|
|
||||||
|
backend_manifest:
|
||||||
|
dashboard: /portal/me/aios/dashboard
|
||||||
|
manifest: /portal/me/aios/manifest
|
||||||
|
|
||||||
|
render_modes:
|
||||||
|
- cards
|
||||||
|
- sidebar
|
||||||
|
- quick_actions
|
||||||
|
|
||||||
|
role_visibility:
|
||||||
|
user:
|
||||||
|
required_modules:
|
||||||
|
- tasks
|
||||||
|
forbidden_modules:
|
||||||
|
- architecture_changes
|
||||||
|
- architect
|
||||||
|
- governance
|
||||||
|
group_admin:
|
||||||
|
required_modules:
|
||||||
|
- tasks
|
||||||
|
- operations
|
||||||
|
- system_health
|
||||||
|
- search
|
||||||
|
- agents
|
||||||
|
forbidden_modules:
|
||||||
|
- architecture_changes
|
||||||
|
- architect
|
||||||
|
- governance
|
||||||
|
owner:
|
||||||
|
required_modules:
|
||||||
|
- tasks
|
||||||
|
- operations
|
||||||
|
- system_health
|
||||||
|
- search
|
||||||
|
- agents
|
||||||
|
- architecture_changes
|
||||||
|
- architect
|
||||||
|
- governance
|
||||||
|
|
||||||
|
api_proxy_required:
|
||||||
|
- /portal/me/aios/dashboard
|
||||||
|
- /portal/me/aios/manifest
|
||||||
|
- /admin/ai-evolution/tasks/overview
|
||||||
|
- /admin/ai-evolution/operations/overview
|
||||||
|
- /admin/ai-evolution/system-health/overview
|
||||||
|
- /admin/ai-evolution/search/overview
|
||||||
|
- /admin/ai-evolution/search/status
|
||||||
|
- /admin/ai-evolution/agents/overview
|
||||||
|
- /admin/ai-evolution/architecture-changes/overview
|
||||||
|
- /admin/ai-evolution/governance/overview
|
||||||
|
- /admin/ai-evolution/governance/timeline
|
||||||
|
|
||||||
|
acceptance:
|
||||||
|
login_required: true
|
||||||
|
casdoor_callback_required: true
|
||||||
|
no_blank_portal_after_login: true
|
||||||
|
show_empty_state_when_api_unavailable: true
|
||||||
|
hide_forbidden_modules: true
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# CMD-20260709-deploy-portal-aios-dashboard
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Deploy AIOS modules into authenticated user portal.
|
||||||
|
|
||||||
|
## Backend
|
||||||
|
|
||||||
|
Enable:
|
||||||
|
|
||||||
|
- Portal AIOS Module Registry
|
||||||
|
- Portal Navigation Builder
|
||||||
|
- Portal Dashboard API
|
||||||
|
|
||||||
|
Routes:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/portal/me/aios/manifest
|
||||||
|
/portal/me/aios/dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
## Frontend
|
||||||
|
|
||||||
|
Integrate portal `/portal/me/`:
|
||||||
|
|
||||||
|
1. Load AIOS dashboard manifest after authentication.
|
||||||
|
2. Render AIOS navigation items.
|
||||||
|
3. Respect role visibility.
|
||||||
|
4. Link routes to deployed AIOS pages.
|
||||||
|
|
||||||
|
## Roles
|
||||||
|
|
||||||
|
Validate:
|
||||||
|
|
||||||
|
User:
|
||||||
|
- Task Center only authorized modules
|
||||||
|
|
||||||
|
Group Admin:
|
||||||
|
- Operations
|
||||||
|
- System Health
|
||||||
|
- Search
|
||||||
|
- Agents
|
||||||
|
|
||||||
|
Owner:
|
||||||
|
- All governance modules
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- AIOS_PORTAL_DASHBOARD_READY
|
||||||
|
- AIOS_PORTAL_NAVIGATION_READY
|
||||||
|
- AIOS_ROLE_VISIBILITY_READY
|
||||||
|
- AIOS_PORTAL_USER_EXPERIENCE_READY
|
||||||
|
|
||||||
|
## Report
|
||||||
|
|
||||||
|
Return:
|
||||||
|
|
||||||
|
- portal URL
|
||||||
|
- frontend version
|
||||||
|
- backend routes
|
||||||
|
- role visibility test result
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# CMD-20260709-enable-autonomous-goal-completion
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Enable AIOS to complete normal development and deployment goals without repeated owner progress queries.
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
Components:
|
||||||
|
|
||||||
|
- Autonomous Goal Completion Supervisor
|
||||||
|
- Task Center
|
||||||
|
- System Health Center
|
||||||
|
- Blocker Memory
|
||||||
|
- Architect Agent
|
||||||
|
- Notification Agent
|
||||||
|
|
||||||
|
## Autonomous Operations
|
||||||
|
|
||||||
|
Allowed:
|
||||||
|
|
||||||
|
- create task plans
|
||||||
|
- assign agents
|
||||||
|
- monitor execution
|
||||||
|
- retry safe failures
|
||||||
|
- trigger self healing
|
||||||
|
- update progress
|
||||||
|
- send completion reports
|
||||||
|
|
||||||
|
## Owner Gate Only
|
||||||
|
|
||||||
|
Require approval for:
|
||||||
|
|
||||||
|
- secrets
|
||||||
|
- paid providers
|
||||||
|
- production baseline promotion
|
||||||
|
- large architecture changes
|
||||||
|
- private data sources
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- AIOS_AUTONOMOUS_GOAL_COMPLETION_READY
|
||||||
|
- AIOS_NO_PROGRESS_QUERY_REQUIRED_READY
|
||||||
|
- AIOS_OWNER_GATE_BOUNDARY_READY
|
||||||
|
- AIOS_AUTOMATED_REPORTING_READY
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# CMD-20260709-finalize-portal-aios-entry
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Complete AIOS Portal unified entry deployment.
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
- Portal AIOS Module Registry
|
||||||
|
- Navigation Builder
|
||||||
|
- Dashboard API
|
||||||
|
- Portal Integration Health
|
||||||
|
- System Health Adapter
|
||||||
|
|
||||||
|
## Deployment Verification
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/portal/me/
|
||||||
|
/portal/me/aios/manifest
|
||||||
|
/portal/me/aios/dashboard
|
||||||
|
/portal/me/aios/health
|
||||||
|
```
|
||||||
|
|
||||||
|
## Role Acceptance
|
||||||
|
|
||||||
|
Validate:
|
||||||
|
|
||||||
|
- user
|
||||||
|
- group_admin
|
||||||
|
- owner
|
||||||
|
|
||||||
|
Expected:
|
||||||
|
|
||||||
|
- correct modules visible
|
||||||
|
- restricted modules hidden
|
||||||
|
- APIs accessible
|
||||||
|
- frontend navigation loaded
|
||||||
|
|
||||||
|
## Health Integration
|
||||||
|
|
||||||
|
Add Portal integration status into system health reporting.
|
||||||
|
|
||||||
|
## Success Signals
|
||||||
|
|
||||||
|
- AIOS_PORTAL_ENTRY_COMPLETE
|
||||||
|
- AIOS_PORTAL_MODULES_VISIBLE
|
||||||
|
- AIOS_PORTAL_RBAC_VERIFIED
|
||||||
|
- AIOS_PORTAL_HEALTH_INTEGRATED
|
||||||
|
|
||||||
|
## Report
|
||||||
|
|
||||||
|
Return:
|
||||||
|
|
||||||
|
- portal URL
|
||||||
|
- frontend deployment version
|
||||||
|
- backend route status
|
||||||
|
- role visibility result
|
||||||
|
- health result
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
# CMD-20260709-fix-user-portal-aios-pages
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Ensure AIOS Web UI pages are visible from user portal after authentication.
|
||||||
|
|
||||||
|
## Symptom
|
||||||
|
|
||||||
|
User portal entry:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://user.cnnetmkt.top/portal/me/
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected:
|
||||||
|
|
||||||
|
- AIOS operations dashboard
|
||||||
|
- Task Center
|
||||||
|
- System Health Center
|
||||||
|
- Search Gateway status
|
||||||
|
- Agent observability
|
||||||
|
- Owner Gate pages (RBAC controlled)
|
||||||
|
|
||||||
|
Actual:
|
||||||
|
|
||||||
|
- login redirects through auth
|
||||||
|
- expected AIOS pages are not visible after login
|
||||||
|
|
||||||
|
## Diagnose
|
||||||
|
|
||||||
|
Check:
|
||||||
|
|
||||||
|
1. Portal frontend routes.
|
||||||
|
2. Portal menu/navigation configuration.
|
||||||
|
3. Backend API proxy routes.
|
||||||
|
4. Authentication callback user permissions.
|
||||||
|
5. RBAC visibility rules.
|
||||||
|
6. Whether V1R4C3 pages were deployed to portal frontend.
|
||||||
|
|
||||||
|
## Required Routes
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/portal/me/
|
||||||
|
/admin/ai-evolution/tasks
|
||||||
|
/admin/ai-evolution/system-health
|
||||||
|
/admin/ai-evolution/operations
|
||||||
|
/admin/ai-evolution/search
|
||||||
|
/admin/ai-evolution/architect/research
|
||||||
|
/admin/ai-evolution/architecture-changes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Required Checks
|
||||||
|
|
||||||
|
- authenticated user can load allowed pages
|
||||||
|
- owner sees governance pages
|
||||||
|
- normal user only sees authorized pages
|
||||||
|
- APIs return expected data
|
||||||
|
- frontend navigation exposes available modules
|
||||||
|
|
||||||
|
## If Missing
|
||||||
|
|
||||||
|
Implement:
|
||||||
|
|
||||||
|
- frontend route registration
|
||||||
|
- navigation menu entries
|
||||||
|
- API proxy configuration
|
||||||
|
- RBAC mapping
|
||||||
|
- deployment refresh
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
Return:
|
||||||
|
|
||||||
|
- visible routes
|
||||||
|
- user role permissions
|
||||||
|
- frontend build version
|
||||||
|
- backend API status
|
||||||
|
- screenshot or URL list
|
||||||
|
|
||||||
|
## Success Signals
|
||||||
|
|
||||||
|
- AIOS_PORTAL_ROUTE_READY
|
||||||
|
- AIOS_WEB_UI_MODULES_VISIBLE
|
||||||
|
- AIOS_RBAC_VISIBILITY_READY
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# CMD-20260709-integrate-aios-portal-modules
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Expose AIOS capabilities through user portal after authentication.
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
Modules:
|
||||||
|
|
||||||
|
- Portal AIOS Module Registry
|
||||||
|
- Portal route manifest API
|
||||||
|
- RBAC visibility mapping
|
||||||
|
- Frontend navigation integration
|
||||||
|
|
||||||
|
## Entry
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://user.cnnetmkt.top/portal/me/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Required visible modules
|
||||||
|
|
||||||
|
Owner:
|
||||||
|
|
||||||
|
- Task Center
|
||||||
|
- Operations Command Center
|
||||||
|
- System Health
|
||||||
|
- Search Governance
|
||||||
|
- Agent Observability
|
||||||
|
- Architecture Changes
|
||||||
|
- Architect Research
|
||||||
|
- Governance
|
||||||
|
|
||||||
|
Group Admin:
|
||||||
|
|
||||||
|
- Task Center
|
||||||
|
- Operations
|
||||||
|
- System Health
|
||||||
|
- Search
|
||||||
|
- Agents
|
||||||
|
|
||||||
|
User:
|
||||||
|
|
||||||
|
- Authorized user modules only
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- AIOS_PORTAL_ROUTE_READY
|
||||||
|
- AIOS_WEB_UI_MODULES_VISIBLE
|
||||||
|
- AIOS_RBAC_VISIBILITY_READY
|
||||||
|
- AIOS_PORTAL_AUTH_INTEGRATION_READY
|
||||||
|
|
||||||
|
## Report
|
||||||
|
|
||||||
|
Return:
|
||||||
|
|
||||||
|
- visible routes by role
|
||||||
|
- frontend build version
|
||||||
|
- API proxy status
|
||||||
|
- deployment status
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# CMD-20260709-verify-portal-production-integration
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Verify AIOS modules are visible in authenticated user portal.
|
||||||
|
|
||||||
|
## Checks
|
||||||
|
|
||||||
|
1. Login flow:
|
||||||
|
|
||||||
|
```text
|
||||||
|
user.cnnetmkt.top/portal/me/
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Manifest:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/portal/me/aios/manifest
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Dashboard:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/portal/me/aios/dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Health:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/portal/me/aios/health
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validate by role
|
||||||
|
|
||||||
|
- user
|
||||||
|
- group_admin
|
||||||
|
- owner
|
||||||
|
|
||||||
|
Check:
|
||||||
|
|
||||||
|
- visible modules
|
||||||
|
- hidden modules
|
||||||
|
- navigation links
|
||||||
|
- API accessibility
|
||||||
|
|
||||||
|
## Success Signals
|
||||||
|
|
||||||
|
- AIOS_PORTAL_ROUTE_READY
|
||||||
|
- AIOS_WEB_UI_MODULES_VISIBLE
|
||||||
|
- AIOS_RBAC_VISIBILITY_READY
|
||||||
|
- AIOS_PORTAL_INTEGRATION_HEALTHY
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"type": "branch",
|
||||||
|
"branch": "patch/user-portal-me-route-diagnostics-20260709",
|
||||||
|
"base": "main",
|
||||||
|
"created_by": "chatgpt-gitops-bot",
|
||||||
|
"created_at": "2026-07-09T09:55:33Z",
|
||||||
|
"direct_main_push": false,
|
||||||
|
"owner_approval_required": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
from active_runtime.app.autonomous_goal_completion import AutonomousGoalCompletionSupervisor
|
||||||
|
|
||||||
|
|
||||||
|
def test_safe_goal_continues_without_user():
|
||||||
|
supervisor = AutonomousGoalCompletionSupervisor()
|
||||||
|
goal = supervisor.create_goal("deploy portal modules")
|
||||||
|
decision = supervisor.decide_next_action(goal.to_dict())
|
||||||
|
assert decision.requires_user is False
|
||||||
|
assert decision.action == "create_task_plan"
|
||||||
|
|
||||||
|
|
||||||
|
def test_production_promotion_requires_owner_gate():
|
||||||
|
supervisor = AutonomousGoalCompletionSupervisor()
|
||||||
|
goal = supervisor.create_goal("promotion")
|
||||||
|
data = goal.to_dict()
|
||||||
|
data["owner_gate_reasons"] = ["production_baseline_promotion"]
|
||||||
|
decision = supervisor.decide_next_action(data)
|
||||||
|
assert decision.requires_owner_gate is True
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_frontend_contract_exists():
|
||||||
|
path = Path("config/portal-aios-frontend-contract.yaml")
|
||||||
|
assert path.exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_contract_contains_roles():
|
||||||
|
text = Path("config/portal-aios-frontend-contract.yaml").read_text(encoding="utf-8")
|
||||||
|
assert "user:" in text
|
||||||
|
assert "group_admin:" in text
|
||||||
|
assert "owner:" in text
|
||||||
|
assert "api_proxy_required:" in text
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
from active_runtime.app.portal_aios_module_registry import PortalAIOSModuleRegistry
|
||||||
|
|
||||||
|
|
||||||
|
def test_owner_sees_governance_modules():
|
||||||
|
manifest = PortalAIOSModuleRegistry().route_manifest("owner")
|
||||||
|
routes = manifest["routes"]
|
||||||
|
assert "/admin/ai-evolution/tasks" in routes
|
||||||
|
assert "/admin/ai-evolution/architecture-changes" in routes
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_does_not_see_owner_modules():
|
||||||
|
manifest = PortalAIOSModuleRegistry().route_manifest("user")
|
||||||
|
routes = manifest["routes"]
|
||||||
|
assert "/admin/ai-evolution/tasks" in routes
|
||||||
|
assert "/admin/ai-evolution/architecture-changes" not in routes
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
from active_runtime.app.portal_aios_navigation import PortalAIOSNavigationBuilder
|
||||||
|
|
||||||
|
|
||||||
|
def test_owner_navigation_contains_governance():
|
||||||
|
result = PortalAIOSNavigationBuilder().build("owner")
|
||||||
|
keys = [item["key"] for item in result["items"]]
|
||||||
|
assert "tasks" in keys
|
||||||
|
assert "governance" in keys
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_navigation_limits_owner_actions():
|
||||||
|
result = PortalAIOSNavigationBuilder().build("user")
|
||||||
|
keys = [item["key"] for item in result["items"]]
|
||||||
|
assert "tasks" in keys
|
||||||
|
assert "governance" not in keys
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from active_runtime.app.portal_integration_health import PortalIntegrationHealth
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_health_detects_missing_frontend_registration():
|
||||||
|
result = PortalIntegrationHealth().check({"routes": ["/portal/me/"]}, {"registered": False})
|
||||||
|
assert result["status"] == "healthy"
|
||||||
|
assert "frontend_navigation_registration" in result["missing"]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from active_runtime.app.portal_system_health_adapter import PortalSystemHealthAdapter
|
||||||
|
|
||||||
|
|
||||||
|
def test_portal_health_adapter_exposes_owner_routes():
|
||||||
|
result = PortalSystemHealthAdapter().build("owner")
|
||||||
|
assert result["component"] == "portal-aios-integration"
|
||||||
|
assert "/admin/ai-evolution/tasks" in result["manifest_routes"]
|
||||||
Reference in New Issue
Block a user