| name | learning-from-corrections |
| description | Use when the user says they adjusted the design in Figma, mentions "fix", "correct", "learn from", "I changed", "diff", "what changed", or wants the system to incorporate manual Figma edits back into the spec. Diffs the current Figma state against the last snapshot, classifies each change as a DS learning or a hardcoded flag, persists learnings, and patches the active recipe. |
{{ACTIVE_RULES}}
Learning From Corrections
Overview
Closes the feedback loop from manual Figma edits back into Bridge's knowledge base. Diffs the live Figma state against the saved snapshot, classifies each correction as a LEARNING (DS-compliant → persisted) or a FLAG (hardcoded → surfaced), updates learnings.json, and auto-patches the active recipe when eligible.
When to Use
Invoke when the user:
- says "I adjusted it", "I fixed it in Figma", "fix", "correct", or "learn from what I changed"
- has an active CSpec in
specs/active/ with a snapshot
Do NOT use if:
- there is no active CSpec — the user should
make first (use generating-figma-design)
- the user wants to start fresh — use
generating-figma-design with a new description
- the user wants to ship — use
shipping-and-archiving
Procedure
Before starting, load:
references/transport-adapter.md (repo-root) — for Figma state re-read
references/compiler-reference.md (repo-root) — for scene graph recompile (if re-executing after fix)
Prerequisites
- Active CSpec in
specs/active/ (abort if missing: "No active CSpec. Run: make <description>")
- Snapshot file exists at
specs/active/{name}-snapshot.json (abort if missing: "No snapshot found. The design must have been generated with make. Run make first.")
- Figma MCP transport available (see
references/transport-adapter.md (repo-root) Section F)
1. Load artifacts
- Read the active CSpec from
specs/active/{name}.cspec.yaml
- Read the snapshot from
specs/active/{name}-snapshot.json
- Read existing learnings from
knowledge-base/learnings.json (create empty structure if file doesn't exist)
- Load
knowledge-base/registries/variables.json for token resolution
2. Re-extract current Figma state
Run a node tree extraction script via Plugin API execution, using the rootNodeId and fileKey from the snapshot's meta.
Console transport:
figma_execute({
code: "return (async function() { ... extraction script with rootNodeId ... })();"
})
Official transport:
use_figma({
fileKey: "{fileKey}",
description: "Re-extract node tree for fix diff",
code: "... extraction script without IIFE wrapper ..."
})
The extraction script walks the node tree and captures: id, name, type, layoutMode, itemSpacing, padding*, cornerRadius, fills, boundVariables, width, height, componentKey, children.
3. Diff snapshot vs current state
Compare the two JSON trees in context. Claude performs this comparison directly.
Match strategy:
- Match nodes by
id (stable across edits)
- For each matched node, compare:
- Layout:
layoutMode, itemSpacing, paddingTop/Bottom/Left/Right
- Visual:
cornerRadius, fills, boundVariables
- Size:
width, height
- Component:
componentKey (detect swapped components)
- Detect added nodes (present in current, absent in snapshot)
- Detect removed nodes (present in snapshot, absent in current)
- Detect property changes (same node, different values)
Ignore:
- Pure name changes (layer renaming)
- Position changes (x, y) unless they indicate a structural move (re-parenting)
4. Classify changes
For each detected change:
Does the new value use a DS token (bound variable)?
-> YES: Classify as LEARNING (DS-compliant correction)
-> NO (hardcoded hex, raw px, unbound): Classify as FLAG (needs attention)
Token resolution: Check boundVariables in the current tree. If the property has a bound variable ID, resolve it against registries/variables.json to get the token name.
5. Save learnings
For each LEARNING-classified change:
-
Determine context:
screenType: from the CSpec's meta.pattern or intent
component: nearest component ancestor name, or the node's own name if it's a component instance
section: parent frame name (e.g., "header", "content", "sidebar")
-
Check for existing learning: Search learnings.json for a learning with matching context + change.property + change.to.token
- If found: increment
signals, append to history
- If not found: create new learning entry
-
Generate rule: Write a human-readable rule describing the preference (e.g., "For settings screens, cards use spacing/medium (not large)")
-
Check promotion: After updating signals, check if any contextual learning qualifies for global promotion:
signals >= 3
- Observations from >= 2 different
screenType values
- No contradiction (same property pointing to different tokens in different learnings)
6. Extract flags
For each FLAG-classified change:
- Create a flag entry with the CSpec name, node description, and what was hardcoded
- Add to
flags array in learnings.json
- Suggest the correct DS token if one exists: "Node {name} uses hardcoded {value}. Consider using {$token} instead."
7. Check recipe patch eligibility
If a recipe was used (check snapshot.meta.recipe):
- Count the number of LEARNING signals from this fix cycle
- If signals >= 2 for the same recipe context:
- Load the recipe file
- Patch the recipe's
graph to reflect the learned changes
- Increment recipe
version, update lastEvolvedAt
- Add entry to recipe
evolution_log
- Report: "Recipe {name} patched with {n} corrections (v{version})"
If a learning is promoted to global scope, scan ALL recipes and patch any where the change applies.
8. Update CSpec
If learnings were extracted (DS-compliant changes):
- Update the active CSpec's token references to match the corrected values
- This ensures the CSpec reflects the final intended design
9. Save learnings file
Write updated learnings.json to knowledge-base/learnings.json.
Update meta.lastUpdated to today's date.
10. Update snapshot
Re-save the snapshot with the current Figma state (so future fix runs diff against the latest corrections, not the original generation).
11. Report
## Fix: {name}
### Changes detected: {total count}
### Learnings extracted: {count}
| # | Context | Property | From | To | Rule |
|---|---------|----------|------|----|------|
| 1 | settings / card | itemSpacing | spacing/large (24) | spacing/medium (16) | Cards in settings use medium spacing |
### Flags: {count}
| # | Node | Issue | Suggestion |
|---|------|-------|------------|
| 1 | StatusBadge | Hardcoded hex #FF5722 | Use $color/text/error/default |
### Recipe patches: {count}
- Recipe "{name}" v{version}: {description of patch}
### Promotions: {count}
- "{rule}" promoted to global (signals: {n}, screenTypes: {list})
### CSpec updated: {yes/no}
{list of CSpec changes if any}
12. Offer next step
Fix complete for {name}.
Learnings: {n} extracted ({n} new, {n} reinforced, {n} promoted)
Flags: {n} hardcoded values flagged
Recipe: {patched | not applicable}
Options:
- Continue editing in Figma, then run `fix` again
- "done" to archive and ship
Transition
- If user wants to continue editing -> they can run
fix again after more changes
- When satisfied -> suggest: "Run:
done" (handled by shipping-and-archiving)
Every correction MUST be classified before `learnings.json` is written.
Unclassified changes are a gate failure.
Every LEARNING MUST reference a token from the current
registries/variables.json / registries/text-styles.json. A
LEARNING that points to a non-existent token is a gate failure.
Every FLAG MUST be surfaced to the user before saving the snapshot.
Red Flags
See the full catalog at references/red-flags-catalog.md (repo-root).
Top flags for this skill:
- "I'll store this hardcoded hex as a LEARNING for later" → Flags are for DS gaps; hardcoded values are FLAGs, not learnings.
- "I can tell what changed without re-reading Figma" → Always re-read Figma. Memory is not a snapshot.
Verification
This skill is gated by references/verification-gates.md (repo-root):
- Gate A — only applies if the fix recompiles the scene graph (rare, optional).
- Gate B — applies if the fix re-executes in Figma.
Evidence to surface: diff summary, classification table, updated learnings.json diff.
The fix flow (decision diagram)
digraph fix_flow {
"User says 'fix'" [shape=doublecircle];
"Diff Figma vs snapshot" [shape=box];
"DS-compliant changes?" [shape=diamond];
"Hardcoded primitives?" [shape=diamond];
"Persist as LEARNING" [shape=box];
"Surface as FLAG" [shape=box style=filled fillcolor=lightyellow];
"Recipe eligible?" [shape=diamond];
"Auto-patch recipe" [shape=box];
"Update snapshot" [shape=doublecircle style=filled fillcolor=lightgreen];
"User says 'fix'" -> "Diff Figma vs snapshot";
"Diff Figma vs snapshot" -> "DS-compliant changes?";
"DS-compliant changes?" -> "Persist as LEARNING" [label="yes"];
"DS-compliant changes?" -> "Hardcoded primitives?" [label="check next"];
"Hardcoded primitives?" -> "Surface as FLAG" [label="yes"];
"Persist as LEARNING" -> "Recipe eligible?";
"Recipe eligible?" -> "Auto-patch recipe" [label="yes"];
"Auto-patch recipe" -> "Update snapshot";
"Recipe eligible?" -> "Update snapshot" [label="no"];
"Surface as FLAG" -> "Update snapshot";
}