Rohan03 commited on
Commit
9980c16
·
verified ·
1 Parent(s): 542f438

Sprint 7: skills/schema.py — SkillCard + SkillGenome with versioning

Browse files
Files changed (1) hide show
  1. purpose_agent/skills/schema.py +140 -0
purpose_agent/skills/schema.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ schema.py — SkillCard and SkillGenome data structures.
3
+
4
+ A SkillCard is the atomic unit of learned procedural knowledge.
5
+ A SkillGenome tracks the evolutionary lineage of a skill across versions.
6
+ """
7
+ from __future__ import annotations
8
+ import time
9
+ import uuid
10
+ from dataclasses import dataclass, field
11
+ from typing import Any
12
+
13
+
14
+ @dataclass
15
+ class SkillCard:
16
+ """
17
+ A single versioned skill — a learned, testable procedure.
18
+
19
+ Lifecycle: candidate → tested → active → (evolved | hibernated | archived)
20
+ """
21
+ id: str = field(default_factory=lambda: uuid.uuid4().hex[:12])
22
+ name: str = ""
23
+ trigger: str = "" # When to apply this skill
24
+ applies_when: list[str] = field(default_factory=list) # Context conditions
25
+ procedure: list[str] = field(default_factory=list) # Step-by-step
26
+ examples: list[dict[str, Any]] = field(default_factory=list) # Input→output examples
27
+ tools: list[str] = field(default_factory=list) # Tools this skill uses
28
+ evals: list[str] = field(default_factory=list) # Test case IDs
29
+ fitness_score: float = 0.5 # 0-1, updated by CI
30
+ version: int = 1
31
+ parent_id: str | None = None # Previous version's ID
32
+ source_memory_ids: list[str] = field(default_factory=list)
33
+ created_at: float = field(default_factory=time.time)
34
+ created_by: str = "" # "consolidation", "retroformer", "human", etc.
35
+ status: str = "candidate" # candidate, tested, active, hibernated, archived
36
+ exportable: bool = True # Can be shared via A2A
37
+
38
+ def to_dict(self) -> dict[str, Any]:
39
+ return {
40
+ "id": self.id, "name": self.name, "trigger": self.trigger,
41
+ "applies_when": self.applies_when, "procedure": self.procedure,
42
+ "examples": self.examples, "tools": self.tools, "evals": self.evals,
43
+ "fitness_score": self.fitness_score, "version": self.version,
44
+ "parent_id": self.parent_id, "source_memory_ids": self.source_memory_ids,
45
+ "created_at": self.created_at, "created_by": self.created_by,
46
+ "status": self.status, "exportable": self.exportable,
47
+ }
48
+
49
+ @classmethod
50
+ def from_dict(cls, d: dict) -> "SkillCard":
51
+ return cls(**{k: v for k, v in d.items() if k in cls.__dataclass_fields__})
52
+
53
+ def to_markdown(self) -> str:
54
+ """Export as human-readable markdown."""
55
+ lines = [f"# Skill: {self.name} (v{self.version})", ""]
56
+ lines.append(f"**Trigger:** {self.trigger}")
57
+ if self.applies_when:
58
+ lines.append(f"**Applies when:** {', '.join(self.applies_when)}")
59
+ lines.append(f"\n## Procedure")
60
+ for i, step in enumerate(self.procedure, 1):
61
+ lines.append(f"{i}. {step}")
62
+ if self.tools:
63
+ lines.append(f"\n**Tools:** {', '.join(self.tools)}")
64
+ lines.append(f"\n**Fitness:** {self.fitness_score:.2f} | **Status:** {self.status}")
65
+ return "\n".join(lines)
66
+
67
+ def evolve(self, new_procedure: list[str] | None = None, new_trigger: str | None = None) -> "SkillCard":
68
+ """Create a new version of this skill (mutation)."""
69
+ return SkillCard(
70
+ name=self.name,
71
+ trigger=new_trigger or self.trigger,
72
+ applies_when=list(self.applies_when),
73
+ procedure=new_procedure or list(self.procedure),
74
+ examples=list(self.examples),
75
+ tools=list(self.tools),
76
+ evals=list(self.evals),
77
+ fitness_score=self.fitness_score * 0.9, # Slight penalty for unproven mutation
78
+ version=self.version + 1,
79
+ parent_id=self.id,
80
+ source_memory_ids=list(self.source_memory_ids),
81
+ created_by="evolution",
82
+ status="candidate",
83
+ )
84
+
85
+
86
+ @dataclass
87
+ class SkillGenome:
88
+ """
89
+ Tracks the evolutionary lineage of a skill across versions.
90
+
91
+ Supports: version history, rollback, fitness tracking over time.
92
+ """
93
+ skill_name: str
94
+ versions: list[SkillCard] = field(default_factory=list)
95
+ active_version_id: str | None = None
96
+
97
+ def add_version(self, card: SkillCard) -> None:
98
+ self.versions.append(card)
99
+
100
+ def promote(self, card_id: str) -> bool:
101
+ """Promote a version to active."""
102
+ for v in self.versions:
103
+ if v.id == card_id:
104
+ # Deactivate current
105
+ if self.active_version_id:
106
+ for vv in self.versions:
107
+ if vv.id == self.active_version_id:
108
+ vv.status = "hibernated"
109
+ v.status = "active"
110
+ self.active_version_id = card_id
111
+ return True
112
+ return False
113
+
114
+ def rollback(self) -> SkillCard | None:
115
+ """Rollback to previous version."""
116
+ if not self.active_version_id:
117
+ return None
118
+ current = next((v for v in self.versions if v.id == self.active_version_id), None)
119
+ if current and current.parent_id:
120
+ parent = next((v for v in self.versions if v.id == current.parent_id), None)
121
+ if parent:
122
+ current.status = "archived"
123
+ parent.status = "active"
124
+ self.active_version_id = parent.id
125
+ return parent
126
+ return None
127
+
128
+ @property
129
+ def active(self) -> SkillCard | None:
130
+ if self.active_version_id:
131
+ return next((v for v in self.versions if v.id == self.active_version_id), None)
132
+ return None
133
+
134
+ @property
135
+ def version_count(self) -> int:
136
+ return len(self.versions)
137
+
138
+ @property
139
+ def fitness_history(self) -> list[float]:
140
+ return [v.fitness_score for v in self.versions]