skillpool 4.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- skillpool/__init__.py +74 -0
- skillpool/__main__.py +6 -0
- skillpool/adapters/__init__.py +8 -0
- skillpool/adapters/base.py +41 -0
- skillpool/adapters/claude_adapter.py +36 -0
- skillpool/adapters/codex_adapter.py +92 -0
- skillpool/adapters/hermes_adapter.py +38 -0
- skillpool/audit/__init__.py +651 -0
- skillpool/bridge/__init__.py +16 -0
- skillpool/bridge/freeze_detector.py +134 -0
- skillpool/bridge/maintenance.py +119 -0
- skillpool/bridge/wal_manager.py +136 -0
- skillpool/clawmem_client.py +176 -0
- skillpool/cli.py +700 -0
- skillpool/combiner/__init__.py +31 -0
- skillpool/combiner/lifecycle.py +453 -0
- skillpool/combiner/models.py +99 -0
- skillpool/config.py +34 -0
- skillpool/cost/__init__.py +111 -0
- skillpool/cost/audit_hash.py +51 -0
- skillpool/cost/budget_tracker.py +66 -0
- skillpool/cost/dashboard.py +189 -0
- skillpool/cost/models.py +129 -0
- skillpool/cost/token_governor.py +264 -0
- skillpool/cost/trace_ceiling.py +38 -0
- skillpool/csdf.py +126 -0
- skillpool/evolver/__init__.py +978 -0
- skillpool/gain/__init__.py +285 -0
- skillpool/gate.py +282 -0
- skillpool/gate_policy/__init__.py +31 -0
- skillpool/gate_policy/incremental.py +157 -0
- skillpool/gate_policy/parser.py +258 -0
- skillpool/gate_policy/state_machine.py +432 -0
- skillpool/graph/__init__.py +14 -0
- skillpool/graph/ppr.py +279 -0
- skillpool/health/__init__.py +73 -0
- skillpool/health/check.py +85 -0
- skillpool/health/degradation.py +90 -0
- skillpool/health/models.py +43 -0
- skillpool/hooks/__init__.py +4 -0
- skillpool/hooks/security_scanner.py +288 -0
- skillpool/lifecycle.py +150 -0
- skillpool/materializer/__init__.py +124 -0
- skillpool/materializer/budget_cropper.py +178 -0
- skillpool/materializer/csdf_loader.py +114 -0
- skillpool/materializer/lazy_loader.py +265 -0
- skillpool/materializer/lifecycle_filter.py +93 -0
- skillpool/materializer/mapper.py +178 -0
- skillpool/materializer/models.py +66 -0
- skillpool/mcp_server.py +2005 -0
- skillpool/monitor/__init__.py +576 -0
- skillpool/monitor/bug_collector.py +392 -0
- skillpool/monitor/defect_classifier.py +218 -0
- skillpool/monitor/self_healing.py +530 -0
- skillpool/monitor/telemetry_bridge.py +197 -0
- skillpool/paradigm/__init__.py +312 -0
- skillpool/paradigm/override.py +285 -0
- skillpool/profile.py +94 -0
- skillpool/quality.py +254 -0
- skillpool/registry/__init__.py +509 -0
- skillpool/registry/models.py +98 -0
- skillpool/resolver/__init__.py +320 -0
- skillpool/resolver/cache.py +103 -0
- skillpool/resolver/circuit_breaker.py +103 -0
- skillpool/resolver/conflict_detector.py +111 -0
- skillpool/resolver/health_filter.py +38 -0
- skillpool/resolver/models.py +154 -0
- skillpool/resolver/rate_limiter.py +48 -0
- skillpool/resolver/skill_graph.py +183 -0
- skillpool/review/__init__.py +242 -0
- skillpool/review/async_queue.py +96 -0
- skillpool/review/checkpoint_runner.py +345 -0
- skillpool/review/models.py +164 -0
- skillpool/review/suspect_marker.py +39 -0
- skillpool/review/veto_evaluator.py +94 -0
- skillpool/router/__init__.py +481 -0
- skillpool/schemas.py +119 -0
- skillpool/synergy/__init__.py +240 -0
- skillpool/synergy/detector.py +5 -0
- skillpool/telemetry.py +126 -0
- skillpool/utils/__init__.py +21 -0
- skillpool/utils/changelog.py +218 -0
- skillpool/utils/logger.py +273 -0
- skillpool/utils/runtime_audit.py +163 -0
- skillpool/utils/time_utils.py +13 -0
- skillpool-4.3.0.dist-info/METADATA +21 -0
- skillpool-4.3.0.dist-info/RECORD +90 -0
- skillpool-4.3.0.dist-info/WHEEL +5 -0
- skillpool-4.3.0.dist-info/entry_points.txt +3 -0
- skillpool-4.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""Telemetry Bridge — Metric emission, evaluation, and alerting.
|
|
2
|
+
|
|
3
|
+
V4.1 module. Bridges runtime metrics to observability backends.
|
|
4
|
+
Architecture constraint:
|
|
5
|
+
- TelemetryBridge is observation only, no control
|
|
6
|
+
- MUST NOT publish versions or replace Audit
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
__all__ = ["TelemetryBridge"]
|
|
12
|
+
|
|
13
|
+
from collections.abc import Callable
|
|
14
|
+
from datetime import UTC, datetime
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TelemetryBridge:
|
|
18
|
+
"""Runtime telemetry emission, five-dimension evaluation, and alerting.
|
|
19
|
+
|
|
20
|
+
Responsibilities:
|
|
21
|
+
1. Collect and emit Metric objects
|
|
22
|
+
2. Evaluate metric sets against five-dimension model
|
|
23
|
+
3. Generate alerts from evaluation results
|
|
24
|
+
4. Provide metric snapshots for external consumers
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, sink: Callable | None = None) -> None:
|
|
28
|
+
"""Initialize TelemetryBridge.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
sink: Optional callable that receives each emitted Metric.
|
|
32
|
+
Signature: sink(metric) -> None
|
|
33
|
+
"""
|
|
34
|
+
self._sink = sink
|
|
35
|
+
self._metrics: list[dict] = []
|
|
36
|
+
self._alerts: list[dict] = []
|
|
37
|
+
|
|
38
|
+
def emit(self, metric) -> None:
|
|
39
|
+
"""Emit a metric to the bridge.
|
|
40
|
+
|
|
41
|
+
Stores the metric internally and forwards to the sink if configured.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
metric: A Metric object (from skillpool.monitor).
|
|
45
|
+
"""
|
|
46
|
+
entry = {
|
|
47
|
+
"timestamp": datetime.now(UTC).isoformat(),
|
|
48
|
+
"metric": metric,
|
|
49
|
+
}
|
|
50
|
+
self._metrics.append(entry)
|
|
51
|
+
|
|
52
|
+
if self._sink is not None:
|
|
53
|
+
from contextlib import suppress
|
|
54
|
+
|
|
55
|
+
with suppress(Exception):
|
|
56
|
+
self._sink(metric)
|
|
57
|
+
|
|
58
|
+
def evaluate(self, metrics: list) -> dict:
|
|
59
|
+
"""Evaluate a set of metrics against the five-dimension model.
|
|
60
|
+
|
|
61
|
+
Dimensions:
|
|
62
|
+
1. Correctness — accuracy of outputs
|
|
63
|
+
2. Efficiency — resource utilization
|
|
64
|
+
3. Robustness — error handling and resilience
|
|
65
|
+
4. Usability — developer experience
|
|
66
|
+
5. Security — vulnerability resistance
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
metrics: List of Metric objects to evaluate.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
FiveDimensionEvaluation-compatible dict with scores per dimension.
|
|
73
|
+
"""
|
|
74
|
+
scores = {
|
|
75
|
+
"correctness": 1.0,
|
|
76
|
+
"efficiency": 1.0,
|
|
77
|
+
"robustness": 1.0,
|
|
78
|
+
"usability": 1.0,
|
|
79
|
+
"security": 1.0,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if not metrics:
|
|
83
|
+
return {
|
|
84
|
+
"dimensions": scores,
|
|
85
|
+
"overall": 1.0,
|
|
86
|
+
"level": "basic",
|
|
87
|
+
"evaluated_at": datetime.now(UTC).isoformat(),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
error_count = 0
|
|
91
|
+
latency_sum = 0.0
|
|
92
|
+
latency_count = 0
|
|
93
|
+
success_count = 0
|
|
94
|
+
total_count = len(metrics)
|
|
95
|
+
|
|
96
|
+
for m in metrics:
|
|
97
|
+
if isinstance(m, dict):
|
|
98
|
+
mtype = m.get("metric_type", "")
|
|
99
|
+
value = m.get("value", 0)
|
|
100
|
+
else:
|
|
101
|
+
mtype = getattr(m, "metric_type", "")
|
|
102
|
+
value = getattr(m, "value", 0)
|
|
103
|
+
|
|
104
|
+
mtype_str = str(mtype).lower()
|
|
105
|
+
|
|
106
|
+
if "error" in mtype_str or "fail" in mtype_str:
|
|
107
|
+
error_count += 1
|
|
108
|
+
elif "latency" in mtype_str or "duration" in mtype_str:
|
|
109
|
+
latency_sum += float(value)
|
|
110
|
+
latency_count += 1
|
|
111
|
+
elif "success" in mtype_str:
|
|
112
|
+
success_count += 1
|
|
113
|
+
|
|
114
|
+
if total_count > 0:
|
|
115
|
+
scores["correctness"] = round(min(5.0, 1.0 + 4.0 * (success_count / total_count)), 2)
|
|
116
|
+
scores["robustness"] = round(min(5.0, max(1.0, 5.0 - error_count * 0.5)), 2)
|
|
117
|
+
|
|
118
|
+
if latency_count > 0:
|
|
119
|
+
avg_latency = latency_sum / latency_count
|
|
120
|
+
scores["efficiency"] = round(min(5.0, max(1.0, 5.0 - avg_latency / 1000)), 2)
|
|
121
|
+
|
|
122
|
+
overall = round(sum(scores.values()) / len(scores), 2)
|
|
123
|
+
|
|
124
|
+
if overall >= 4.0:
|
|
125
|
+
level = "excellent"
|
|
126
|
+
elif overall >= 3.0:
|
|
127
|
+
level = "good"
|
|
128
|
+
elif overall >= 2.0:
|
|
129
|
+
level = "acceptable"
|
|
130
|
+
else:
|
|
131
|
+
level = "basic"
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
"dimensions": scores,
|
|
135
|
+
"overall": overall,
|
|
136
|
+
"level": level,
|
|
137
|
+
"evaluated_at": datetime.now(UTC).isoformat(),
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
def check_alerts(self, evaluation: dict) -> list:
|
|
141
|
+
"""Generate alerts from evaluation results.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
evaluation: FiveDimensionEvaluation dict from evaluate().
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
List of Alert dicts with severity and message.
|
|
148
|
+
"""
|
|
149
|
+
alerts = []
|
|
150
|
+
dimensions = evaluation.get("dimensions", {})
|
|
151
|
+
overall = evaluation.get("overall", 1.0)
|
|
152
|
+
|
|
153
|
+
if overall < 2.0:
|
|
154
|
+
alerts.append(
|
|
155
|
+
{
|
|
156
|
+
"severity": "critical",
|
|
157
|
+
"message": f"Overall evaluation critically low: {overall}",
|
|
158
|
+
"dimension": "overall",
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
for dim_name, score in dimensions.items():
|
|
163
|
+
if score < 2.0:
|
|
164
|
+
alerts.append(
|
|
165
|
+
{
|
|
166
|
+
"severity": "warning",
|
|
167
|
+
"message": f"Dimension '{dim_name}' below threshold: {score}",
|
|
168
|
+
"dimension": dim_name,
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
elif score < 3.0:
|
|
172
|
+
alerts.append(
|
|
173
|
+
{
|
|
174
|
+
"severity": "info",
|
|
175
|
+
"message": f"Dimension '{dim_name}' needs attention: {score}",
|
|
176
|
+
"dimension": dim_name,
|
|
177
|
+
}
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
self._alerts.extend(alerts)
|
|
181
|
+
return alerts
|
|
182
|
+
|
|
183
|
+
def snapshot(self) -> dict:
|
|
184
|
+
"""Return a snapshot of all collected metrics and alerts.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
Dict with 'metrics', 'alerts', and 'counts' keys.
|
|
188
|
+
"""
|
|
189
|
+
return {
|
|
190
|
+
"metrics": list(self._metrics),
|
|
191
|
+
"alerts": list(self._alerts),
|
|
192
|
+
"counts": {
|
|
193
|
+
"metrics": len(self._metrics),
|
|
194
|
+
"alerts": len(self._alerts),
|
|
195
|
+
},
|
|
196
|
+
"snapshot_at": datetime.now(UTC).isoformat(),
|
|
197
|
+
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ParadigmRegistry — 4D 范式 Skill 注册 + 查询。
|
|
3
|
+
|
|
4
|
+
4 个 4D 范式:
|
|
5
|
+
- DocsDD: 文档驱动开发(需求→规格→验收)
|
|
6
|
+
- SDD: 规格驱动设计(接口→架构→评审)
|
|
7
|
+
- BDD: 行为驱动开发(场景→测试→实现)
|
|
8
|
+
- TDD: 测试驱动开发(红→绿→重构)
|
|
9
|
+
|
|
10
|
+
每个范式注册为 CSDF 文档,ParadigmRegistry 负责存储和查询。
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from enum import StrEnum
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
import yaml
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Paradigm(StrEnum):
|
|
24
|
+
DOCS_DD = "docsdd"
|
|
25
|
+
SDD = "sdd"
|
|
26
|
+
BDD = "bdd"
|
|
27
|
+
TDD = "tdd"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# 4 个范式的 CSDF 定义
|
|
31
|
+
PARADigm_CSDFS: dict[Paradigm, dict] = {
|
|
32
|
+
Paradigm.DOCS_DD: {
|
|
33
|
+
"id": "paradigm-docsdd",
|
|
34
|
+
"name": "DocsDD — 文档驱动开发",
|
|
35
|
+
"version": "1.0.0",
|
|
36
|
+
"dimension": "D11",
|
|
37
|
+
"paradigm": "docsdd",
|
|
38
|
+
"weight": 0.2,
|
|
39
|
+
"description": "文档先行:需求文档 → 规格文档 → 验收标准",
|
|
40
|
+
"checklist": [
|
|
41
|
+
{"item": "需求文档完整", "priority": "high"},
|
|
42
|
+
{"item": "规格文档对齐需求", "priority": "high"},
|
|
43
|
+
{"item": "验收标准可量化", "priority": "medium"},
|
|
44
|
+
],
|
|
45
|
+
"required_agent_capabilities": {"file_system"},
|
|
46
|
+
"min_trust_level": 1,
|
|
47
|
+
"lifecycle_state": "ACTIVE",
|
|
48
|
+
"input_schema": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"properties": {
|
|
51
|
+
"requirements": {"type": "string", "description": "需求描述"},
|
|
52
|
+
"scope": {"type": "string", "description": "范围定义"},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
"output_schema": {
|
|
56
|
+
"required": {"doc": "文档路径", "acceptance": "验收标准"},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
Paradigm.SDD: {
|
|
60
|
+
"id": "paradigm-sdd",
|
|
61
|
+
"name": "SDD — 规格驱动设计",
|
|
62
|
+
"version": "1.0.0",
|
|
63
|
+
"dimension": "D11",
|
|
64
|
+
"paradigm": "sdd",
|
|
65
|
+
"weight": 0.2,
|
|
66
|
+
"description": "规格先行:接口定义 → 架构设计 → 同行评审",
|
|
67
|
+
"checklist": [
|
|
68
|
+
{"item": "接口定义明确", "priority": "high"},
|
|
69
|
+
{"item": "架构图更新", "priority": "high"},
|
|
70
|
+
{"item": "同行评审通过", "priority": "medium"},
|
|
71
|
+
],
|
|
72
|
+
"required_agent_capabilities": {"file_system", "python"},
|
|
73
|
+
"min_trust_level": 2,
|
|
74
|
+
"lifecycle_state": "ACTIVE",
|
|
75
|
+
"input_schema": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"properties": {
|
|
78
|
+
"spec": {"type": "string", "description": "规格文档路径"},
|
|
79
|
+
"interfaces": {"type": "array", "description": "接口列表"},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
"output_schema": {
|
|
83
|
+
"required": {"design_doc": "设计文档", "interfaces": "接口定义"},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
Paradigm.BDD: {
|
|
87
|
+
"id": "paradigm-bdd",
|
|
88
|
+
"name": "BDD — 行为驱动开发",
|
|
89
|
+
"version": "1.0.0",
|
|
90
|
+
"dimension": "D7",
|
|
91
|
+
"paradigm": "bdd",
|
|
92
|
+
"weight": 0.3,
|
|
93
|
+
"description": "行为先行:场景定义 → 测试用例 → 实现",
|
|
94
|
+
"checklist": [
|
|
95
|
+
{"item": "场景覆盖完整", "priority": "high"},
|
|
96
|
+
{"item": "测试用例可执行", "priority": "high"},
|
|
97
|
+
{"item": "Given-When-Then 格式", "priority": "medium"},
|
|
98
|
+
],
|
|
99
|
+
"required_agent_capabilities": {"bash", "file_system", "python"},
|
|
100
|
+
"min_trust_level": 2,
|
|
101
|
+
"lifecycle_state": "ACTIVE",
|
|
102
|
+
"input_schema": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"properties": {
|
|
105
|
+
"scenarios": {"type": "array", "description": "BDD 场景列表"},
|
|
106
|
+
"feature": {"type": "string", "description": "Feature 名称"},
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
"output_schema": {
|
|
110
|
+
"required": {"test_file": "测试文件路径", "coverage": "覆盖率"},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
Paradigm.TDD: {
|
|
114
|
+
"id": "paradigm-tdd",
|
|
115
|
+
"name": "TDD — 测试驱动开发",
|
|
116
|
+
"version": "1.0.0",
|
|
117
|
+
"dimension": "D7",
|
|
118
|
+
"paradigm": "tdd",
|
|
119
|
+
"weight": 0.3,
|
|
120
|
+
"description": "测试先行:红(写失败测试)→ 绿(最小实现)→ 重构",
|
|
121
|
+
"checklist": [
|
|
122
|
+
{"item": "测试先于实现", "priority": "high"},
|
|
123
|
+
{"item": "红→绿→重构循环", "priority": "high"},
|
|
124
|
+
{"item": "覆盖率 ≥ 90%", "priority": "medium"},
|
|
125
|
+
],
|
|
126
|
+
"required_agent_capabilities": {"bash", "file_system", "python"},
|
|
127
|
+
"min_trust_level": 2,
|
|
128
|
+
"lifecycle_state": "ACTIVE",
|
|
129
|
+
"input_schema": {
|
|
130
|
+
"type": "object",
|
|
131
|
+
"properties": {
|
|
132
|
+
"test_spec": {"type": "string", "description": "测试规格"},
|
|
133
|
+
"target": {"type": "string", "description": "目标模块"},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
"output_schema": {
|
|
137
|
+
"required": {"test_file": "测试文件", "impl_file": "实现文件"},
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@dataclass
|
|
144
|
+
class ParadigmEntry:
|
|
145
|
+
"""注册表中的一条范式记录。"""
|
|
146
|
+
|
|
147
|
+
paradigm: Paradigm
|
|
148
|
+
csdf: dict
|
|
149
|
+
registered_at: str = ""
|
|
150
|
+
gate_file: Optional[str] = None
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class OverrideLevel(StrEnum):
|
|
154
|
+
"""Emergency override severity levels."""
|
|
155
|
+
|
|
156
|
+
WARN = "WARN"
|
|
157
|
+
DEGRADE = "DEGRADE"
|
|
158
|
+
QUARANTINE = "QUARANTINE"
|
|
159
|
+
KILL = "KILL"
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@dataclass
|
|
163
|
+
class EmergencyOverride:
|
|
164
|
+
"""An emergency override applied to a paradigm or skill."""
|
|
165
|
+
|
|
166
|
+
level: OverrideLevel
|
|
167
|
+
target: str # paradigm name or skill_id
|
|
168
|
+
reason: str
|
|
169
|
+
applied_at: str = ""
|
|
170
|
+
revoked: bool = False
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class ParadigmRegistry:
|
|
174
|
+
"""4D 范式注册表 — 存储、查询、校验。
|
|
175
|
+
|
|
176
|
+
Usage:
|
|
177
|
+
registry = ParadigmRegistry()
|
|
178
|
+
registry.register_defaults()
|
|
179
|
+
entry = registry.get(Paradigm.TDD)
|
|
180
|
+
all_entries = registry.list_all()
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
def __init__(self, persist_dir: Optional[Path] = None):
|
|
184
|
+
self._entries: dict[Paradigm, ParadigmEntry] = {}
|
|
185
|
+
self.persist_dir = persist_dir
|
|
186
|
+
self._overrides: list[EmergencyOverride] = []
|
|
187
|
+
|
|
188
|
+
def register(self, paradigm: Paradigm, csdf: dict) -> ParadigmEntry:
|
|
189
|
+
"""注册一个范式 CSDF。"""
|
|
190
|
+
from datetime import datetime, timezone
|
|
191
|
+
|
|
192
|
+
entry = ParadigmEntry(
|
|
193
|
+
paradigm=paradigm,
|
|
194
|
+
csdf=csdf,
|
|
195
|
+
registered_at=datetime.now(timezone.utc).isoformat(),
|
|
196
|
+
)
|
|
197
|
+
self._entries[paradigm] = entry
|
|
198
|
+
if self.persist_dir:
|
|
199
|
+
self._save_to_disk(entry)
|
|
200
|
+
return entry
|
|
201
|
+
|
|
202
|
+
def register_defaults(self) -> None:
|
|
203
|
+
"""注册 4 个默认 4D 范式。"""
|
|
204
|
+
for p, csdf in PARADigm_CSDFS.items():
|
|
205
|
+
self.register(p, csdf)
|
|
206
|
+
|
|
207
|
+
def get(self, paradigm: Paradigm) -> Optional[ParadigmEntry]:
|
|
208
|
+
"""查询指定范式。"""
|
|
209
|
+
return self._entries.get(paradigm)
|
|
210
|
+
|
|
211
|
+
def get_by_name(self, name: str) -> Optional[ParadigmEntry]:
|
|
212
|
+
"""按名称查询范式(大小写不敏感)。"""
|
|
213
|
+
name_lower = name.lower()
|
|
214
|
+
for p, entry in self._entries.items():
|
|
215
|
+
if p.value == name_lower or name_lower in entry.csdf.get("name", "").lower():
|
|
216
|
+
return entry
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
def list_all(self) -> list[ParadigmEntry]:
|
|
220
|
+
"""列出所有已注册范式。"""
|
|
221
|
+
return list(self._entries.values())
|
|
222
|
+
|
|
223
|
+
def unregister(self, paradigm: Paradigm) -> bool:
|
|
224
|
+
"""注销一个范式。"""
|
|
225
|
+
if paradigm in self._entries:
|
|
226
|
+
del self._entries[paradigm]
|
|
227
|
+
return True
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
def validate(self, paradigm: Paradigm) -> list[str]:
|
|
231
|
+
"""校验范式 CSDF 完整性,返回错误列表。"""
|
|
232
|
+
entry = self._entries.get(paradigm)
|
|
233
|
+
if not entry:
|
|
234
|
+
return [f"paradigm {paradigm} not registered"]
|
|
235
|
+
|
|
236
|
+
errors = []
|
|
237
|
+
csdf = entry.csdf
|
|
238
|
+
required_fields = ["id", "name", "version", "dimension", "paradigm", "checklist"]
|
|
239
|
+
for f in required_fields:
|
|
240
|
+
if f not in csdf or not csdf[f]:
|
|
241
|
+
errors.append(f"missing or empty field: {f}")
|
|
242
|
+
|
|
243
|
+
checklist = csdf.get("checklist", [])
|
|
244
|
+
if checklist:
|
|
245
|
+
for i, item in enumerate(checklist):
|
|
246
|
+
if not item.get("item") and not item.get("description"):
|
|
247
|
+
errors.append(f"checklist[{i}] missing item/description")
|
|
248
|
+
if not item.get("priority") and not item.get("severity"):
|
|
249
|
+
errors.append(f"checklist[{i}] missing priority/severity")
|
|
250
|
+
|
|
251
|
+
return errors
|
|
252
|
+
|
|
253
|
+
def apply_override(self, level: OverrideLevel, target: str, reason: str) -> EmergencyOverride:
|
|
254
|
+
"""Apply an emergency override to a paradigm or skill.
|
|
255
|
+
|
|
256
|
+
WARN: Log warning, continue operation.
|
|
257
|
+
DEGRADE: Reduce functionality (e.g., skip non-critical checks).
|
|
258
|
+
QUARANTINE: Isolate the target from active processing.
|
|
259
|
+
KILL: Immediately stop all operations for the target.
|
|
260
|
+
"""
|
|
261
|
+
from datetime import datetime, timezone
|
|
262
|
+
|
|
263
|
+
override = EmergencyOverride(
|
|
264
|
+
level=level,
|
|
265
|
+
target=target,
|
|
266
|
+
reason=reason,
|
|
267
|
+
applied_at=datetime.now(timezone.utc).isoformat(),
|
|
268
|
+
)
|
|
269
|
+
self._overrides.append(override)
|
|
270
|
+
|
|
271
|
+
# For QUARANTINE/KILL, unregister the paradigm if it matches
|
|
272
|
+
if level in (OverrideLevel.QUARANTINE, OverrideLevel.KILL):
|
|
273
|
+
for p in list(self._entries.keys()):
|
|
274
|
+
if p.value == target.lower():
|
|
275
|
+
self._entries[p].csdf["lifecycle_state"] = (
|
|
276
|
+
"QUARANTINED" if level == OverrideLevel.QUARANTINE else "KILLED"
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
return override
|
|
280
|
+
|
|
281
|
+
def revoke_override(self, target: str) -> bool:
|
|
282
|
+
"""Revoke the most recent active override for a target."""
|
|
283
|
+
for override in reversed(self._overrides):
|
|
284
|
+
if override.target == target and not override.revoked:
|
|
285
|
+
override.revoked = True
|
|
286
|
+
# Restore lifecycle state if paradigm exists
|
|
287
|
+
for p, entry in self._entries.items():
|
|
288
|
+
if p.value == target.lower() and entry.csdf.get("lifecycle_state") in ("QUARANTINED", "KILLED"):
|
|
289
|
+
entry.csdf["lifecycle_state"] = "ACTIVE"
|
|
290
|
+
return True
|
|
291
|
+
return False
|
|
292
|
+
|
|
293
|
+
def get_active_overrides(self) -> list[EmergencyOverride]:
|
|
294
|
+
"""Get all active (non-revoked) overrides."""
|
|
295
|
+
return [o for o in self._overrides if not o.revoked]
|
|
296
|
+
|
|
297
|
+
def get_override_level(self, target: str) -> Optional[OverrideLevel]:
|
|
298
|
+
"""Get the highest active override level for a target."""
|
|
299
|
+
active = [o for o in self._overrides if o.target == target and not o.revoked]
|
|
300
|
+
if not active:
|
|
301
|
+
return None
|
|
302
|
+
# Priority: KILL > QUARANTINE > DEGRADE > WARN
|
|
303
|
+
priority = {OverrideLevel.KILL: 4, OverrideLevel.QUARANTINE: 3, OverrideLevel.DEGRADE: 2, OverrideLevel.WARN: 1}
|
|
304
|
+
return max(active, key=lambda o: priority[o.level]).level
|
|
305
|
+
|
|
306
|
+
def _save_to_disk(self, entry: ParadigmEntry) -> None:
|
|
307
|
+
"""持久化到 YAML 文件。"""
|
|
308
|
+
if not self.persist_dir:
|
|
309
|
+
return
|
|
310
|
+
self.persist_dir.mkdir(parents=True, exist_ok=True)
|
|
311
|
+
path = self.persist_dir / f"{entry.paradigm.value}.yaml"
|
|
312
|
+
path.write_text(yaml.dump(entry.csdf, default_flow_style=False, allow_unicode=True))
|