Documents Skill Management in CLOUD_DEPLOYMENT.md (cloud-skill store, per-host entitlement, incremental sync, local authoring/override, inventory report, skills:admin scope) and applies ruff check/format to all touched modules. All tasks complete; full non-integration suite green (593 passed) and openspec validate --strict passes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ Authoring tools (``create_skill``/``update_skill``/``delete_skill``) dispatch by
|
||||
origin (design D10): they edit/delete local skills and create/update/remove
|
||||
local overrides for cloud skills, never writing to the synced store.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
@@ -140,9 +141,7 @@ def skill_tool_handlers(
|
||||
except ValueError as exc:
|
||||
return _error_response(SkillAuthoringError(str(exc)))
|
||||
if view.is_local_skill(skill_id):
|
||||
stored = local.update_local(
|
||||
_with_id(skill, skill_id)
|
||||
)
|
||||
stored = local.update_local(_with_id(skill, skill_id))
|
||||
return {
|
||||
"ok": True,
|
||||
"skill": _skill_to_full_dict(stored),
|
||||
@@ -308,7 +307,8 @@ def _build_skill(payload: dict[str, Any]) -> KnowledgeSkill | FlowTemplateSkill:
|
||||
for step in (payload.get("steps") or [])
|
||||
]
|
||||
parameters = {
|
||||
str(name): dict(schema) for name, schema in (payload.get("parameters") or {}).items()
|
||||
str(name): dict(schema)
|
||||
for name, schema in (payload.get("parameters") or {}).items()
|
||||
}
|
||||
return FlowTemplateSkill(metadata=meta, steps=steps, parameters=parameters)
|
||||
|
||||
|
||||
@@ -9,16 +9,15 @@ Override semantics (design D8/D9/D11): a local override shadows the cloud
|
||||
skill at read time, wins against sync updates, and is reported with
|
||||
``origin = "cloud"`` and ``locally_overridden = true`` while it shadows.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
|
||||
from skills_learning.models import (
|
||||
FlowTemplateSkill,
|
||||
KnowledgeSkill,
|
||||
Skill,
|
||||
SkillMetadata,
|
||||
)
|
||||
from storage.local_skills import LocalSkillStore
|
||||
@@ -72,9 +71,7 @@ class SkillCatalogView:
|
||||
if meta.id in override_ids:
|
||||
override = self._local.get_override(meta.id)
|
||||
if override is not None:
|
||||
summaries.append(
|
||||
SkillSummary(override.metadata, "cloud", True)
|
||||
)
|
||||
summaries.append(SkillSummary(override.metadata, "cloud", True))
|
||||
continue
|
||||
summaries.append(SkillSummary(meta, "cloud", False))
|
||||
|
||||
@@ -96,9 +93,7 @@ class SkillCatalogView:
|
||||
if meta.id in override_ids:
|
||||
override = self._local.get_override(meta.id)
|
||||
if override is not None:
|
||||
summaries.append(
|
||||
SkillSummary(override.metadata, "cloud", True)
|
||||
)
|
||||
summaries.append(SkillSummary(override.metadata, "cloud", True))
|
||||
continue
|
||||
summaries.append(SkillSummary(meta, "cloud", False))
|
||||
|
||||
@@ -148,9 +143,7 @@ class SkillCatalogView:
|
||||
return self._local.has_override(cloud_skill_id)
|
||||
|
||||
|
||||
def _tools_valid(
|
||||
skill: FlowTemplateSkill, registered_tools: set[str]
|
||||
) -> bool:
|
||||
def _tools_valid(skill: FlowTemplateSkill, registered_tools: set[str]) -> bool:
|
||||
return all(step.tool_name in registered_tools for step in skill.steps)
|
||||
|
||||
|
||||
|
||||
+3
-6
@@ -7,6 +7,7 @@ truth" contract is enforced by that import boundary.
|
||||
|
||||
Push (webhook) is optional; the baseline pull loop is correct standalone.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
@@ -185,9 +186,7 @@ class CloudApiSkillClient:
|
||||
response.raise_for_status()
|
||||
return _parse_cloud_sync_payload(response.json())
|
||||
|
||||
def report_inventory(
|
||||
self, host_id: str, inventory: list[dict[str, Any]]
|
||||
) -> None:
|
||||
def report_inventory(self, host_id: str, inventory: list[dict[str, Any]]) -> None:
|
||||
"""Best-effort local-skill inventory report to the Cloud (design D7)."""
|
||||
response = self._ensure_client().post(
|
||||
f"{self.base_url}/internal/v1/hosts/{host_id}/skills/inventory",
|
||||
@@ -312,9 +311,7 @@ class SkillSyncRunner:
|
||||
subscription_id, since_version=since_version
|
||||
)
|
||||
except Exception as exc:
|
||||
log.warning(
|
||||
"skill sync fetch failed for %s: %s", subscription_id, exc
|
||||
)
|
||||
log.warning("skill sync fetch failed for %s: %s", subscription_id, exc)
|
||||
self.store._set_subscription_state(
|
||||
subscription_id,
|
||||
last_error=f"{type(exc).__name__}: {exc}",
|
||||
|
||||
Reference in New Issue
Block a user