Matrix City - Skills
Previously
In the first post we built ten characters. Each one generated from three overlapping personality systems such as Big Five, Enneagram, MBTI, and were injected into a feudal-industrial world set in 1875. The pipeline produced identity, appearance, backstory, and abilities.
One such character is Ruth Pemberton, a Type 9w1 ledger clerk from Ashwick, a woman shaped by a colliery collapse that killed her brother, who retreated into numbers because columns don't argue.
Current status is we have ten character with rich internal lives and compelling backstories.
However, none of them can do anything.
The biography reads well but the character has no agency. In this devlog, we are going to talk giving them agency as well as making that agency expensive.
The Naive Version
The obvious move is a flat list.
{
"skills": ["blacksmithing", "negotiation", "reading"]
}
This is what most simulations are probably doing. Assign a tag and a number next to it. Blacksmithing: 7. Negotiation: 4.
The problem is that every skill is equally easy. A novice blacksmith and an expert blacksmith are the same object with a different number attached. This system misses the difference in cognitive architectures. The novice thinks about every hammer stroke which the expert's hands already know. The novice is exhausted after an hour but the expert enters flow state and comes out energized.
If you want emergent behavior, you need skills that behave like skills actually behave. Skills can be considered as things that transform with practice, that have prerequisites, that cost the body something to perform, and that decay when neglected.
Three Learning Models, Stacked
No single learning model captures everything that matters. So we stack three.
Fitts & Posner tracks how execution changes:
- Cognitive — you think about every step. Deliberate, effortful, error-prone.
- Associative — you're refining. Chunking sub-movements together. Smoothing.
- Autonomous — you're not thinking anymore. The skill runs itself.
Dreyfus tracks how perception changes:
| Level | Stage | Behavior |
|---|---|---|
| 1 | Novice | Follows rules literally |
| 2 | Advanced Beginner | Recognizes situations |
| 3 | Competent | Plans, prioritizes |
| 4 | Proficient | Sees the whole field |
| 5 | Expert | Acts from intuition |
A Level 1 blacksmith follows the recipe. A Level 5 blacksmith feels when the metal is ready.
Bloom's Taxonomy sets the ceiling on how far you could theoretically go:
- Remember — recall facts
- Understand — explain concepts
- Apply — use in new situations
- Analyze — break down structure
- Evaluate — judge quality
- Create — produce something new
A laborer's ceiling on political theory might be 3 (Apply). An aristocrat's ceiling on manual labor might be 3. Different people, different ceilings.
The Skill Object
All three models collapse into a single progression tracker:
class SkillProgression(BaseModel):
fitts_stage: "cognitive" | "associative" | "autonomous"
dreyfus_level: 1 | 2 | 3 | 4 | 5
bloom_ceiling: 1 | 2 | 3 | 4 | 5 | 6
proficiency: float # 0–1
practice_count: int
success_history: List[bool]
automaticity: float # 0–1
decay_rate: float
Every skill is a living object that grows with practice and decays without it. The object changes how it's represented internally as mastery increases.
That last part matters.
A skill at the cognitive stage is a DeclarativeRepresentation which is a prompt template interpreted by the LLM at runtime. At the associative stage, it becomes a CompiledRepresentation i.e. a named Python function. At the autonomous stage, it's an AutomaticRepresentation simply a fast reflexive function that bypasses the language model entirely.
A novice skill thinks. A mastered skill acts.
The Skill Graph
Skills form a Directed Acyclic Graph. Prerequisites point upward, mastery unlocks downward.
gathering (root)
└─ spark_creation
└─ fire
└─ basic_cooking
└─ grill
hand_movements (root)
└─ measurement
└─ woodworking
The graph validates itself on construction. Every edge is checked for cycles via DFS with path tracking. Graphs are built at the run-time.
class SkillGraph:
def __init__(self, skills: Dict[str, Skill]):
self.nodes = {sid: SkillNode(skill) for sid, skill in skills.items()}
self._build_edges()
self._detect_cycles()
How Skills Grow
After every execution, the skill updates:
def update_skill_progression(skill, success):
# Proficiency: sliding window over last 50 attempts
window = success_history[-50:]
new_proficiency = sum(window) / len(window)
# Dreyfus promotion at thresholds
# 0.25 → level 2, 0.45 → level 3, 0.65 → level 4, 0.85 → level 5
# Fitts promotion
# 0.40 proficiency → associative, 0.75 → autonomous
# Automaticity creeps up
automaticity += 0.005 * proficiency
Proficiency is computed from a sliding window of recent attempts. Dreyfus levels promote at fixed thresholds, but only after enough practice reps. Automaticity rises slowly, gated by proficiency.
And skills that aren't practiced decay. Every skill carries a decay_rate. A blacksmith who stops working the forge for a year doesn't come back at full strength.
Use it or lose it.
LLM Skill Generation
The skill schema is fed to the language model alongside the character's personality, lore, and world context. The LLM generates skills consistent with the life.
Ruth Pemberton is a ledger clerk. Type 9w1. Unhealthy. ISFJ. The LLM doesn't give her swordfighting. It gives her ledger-keeping at Dreyfus 4, conflict avoidance at Dreyfus 3, public speaking at Dreyfus 1. The skills are the personality expressed as capability. The gaps are the personality expressed as limitation.
What's Next
Part B connects skills to bodies through five physiological attributes: health, mood, stress, hunger, energy.