660 lines
18 KiB
Markdown
660 lines
18 KiB
Markdown
# Copy-on-Update Versioning Pattern
|
|
|
|
**Version**: 1.0.0
|
|
**Purpose**: Non-destructive component and screen modifications
|
|
**Applies to**: component-creator, screen-scaffolder, enhancer
|
|
**Does NOT apply to**: component-expander (uses in-place updates)
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
When modifying existing components or screens, create versioned copies instead of overwriting originals. This ensures non-destructive editing and preserves component history.
|
|
|
|
**Core Principle**: Always start with UXM file, even if user mentions `.md` file.
|
|
|
|
---
|
|
|
|
## When to Use Copy-on-Update
|
|
|
|
### ✅ Use Copy-on-Update For:
|
|
|
|
1. **Creating component that already exists** (component-creator)
|
|
- User: "Create a submit button"
|
|
- Existing: `submit-button.uxm` already exists
|
|
- Action: Ask user, offer to create versioned copy
|
|
|
|
2. **Enhancing component fidelity** (enhancer)
|
|
- User: "Enhance button to production fidelity"
|
|
- Existing: `button.uxm` (fidelity=sketch)
|
|
- Action: Create `button-v2.uxm` (fidelity=production)
|
|
|
|
3. **Creating screen that already exists** (screen-scaffolder)
|
|
- User: "Create a login screen"
|
|
- Existing: `login-screen.uxm` already exists
|
|
- Action: Ask user, offer to create versioned copy
|
|
|
|
4. **Modifying layout/structure** (any skill)
|
|
- User: "Change button.md to use different colors"
|
|
- Existing: `button.uxm` exists
|
|
- Action: Create `button-v2.uxm` and `button-v2.md` with changes
|
|
|
|
### ❌ Do NOT Use Copy-on-Update For:
|
|
|
|
1. **Adding states to components** (component-expander only)
|
|
- User: "Add hover state to my button"
|
|
- Action: Modify `button.uxm` in place (same component, enhanced)
|
|
- Reason: Adding states enhances the same component ID
|
|
|
|
---
|
|
|
|
## Version Numbering
|
|
|
|
### File Naming Pattern
|
|
|
|
```
|
|
Original: {base-name}.uxm (e.g., submit-button.uxm)
|
|
Version 2: {base-name}-v2.uxm (e.g., submit-button-v2.uxm)
|
|
Version 3: {base-name}-v3.uxm (e.g., submit-button-v3.uxm)
|
|
```
|
|
|
|
**Rules**:
|
|
- Original file has NO version suffix
|
|
- Versions start at `-v2` (not `-v1`)
|
|
- Version number matches across `.uxm` and `.md` files
|
|
- For screens: All three files use same suffix (`.uxm`, `.md`, `.rendered.md`)
|
|
|
|
### Component ID Pattern
|
|
|
|
The `id` field in the `.uxm` file must match the filename:
|
|
|
|
```json
|
|
// Original: submit-button.uxm
|
|
{
|
|
"id": "submit-button",
|
|
"version": "1.0.0"
|
|
}
|
|
|
|
// Version 2: submit-button-v2.uxm
|
|
{
|
|
"id": "submit-button-v2",
|
|
"version": "1.1.0"
|
|
}
|
|
```
|
|
|
|
### Semantic Versioning
|
|
|
|
**Format**: `MAJOR.MINOR.PATCH` (e.g., `1.2.3`)
|
|
|
|
**Increment rules**:
|
|
- **Minor version** (1.X.0): New features, fidelity enhancements, layout changes
|
|
- Original: 1.0.0 → First copy: 1.1.0 → Second copy: 1.2.0
|
|
- **Major version** (X.0.0): Breaking changes (manual only, not auto-incremented)
|
|
- **Patch version** (1.0.X): Not used in copy-on-update pattern
|
|
|
|
**Copy-on-update always increments minor version**.
|
|
|
|
---
|
|
|
|
## Detection Algorithm
|
|
|
|
### Step 1: Parse Component/Screen Name
|
|
|
|
Extract the base name from user request, ignoring `.uxm` or `.md` extensions:
|
|
|
|
```bash
|
|
# Examples:
|
|
"Update submit-button.md" → componentName="submit-button"
|
|
"Enhance button to production" → componentName="button"
|
|
"Create a login screen" → screenName="login-screen"
|
|
```
|
|
|
|
### Step 2: Check if Original Exists
|
|
|
|
```bash
|
|
# For components:
|
|
if [[ -f "./fluxwing/components/${componentName}.uxm" ]]; then
|
|
echo "Component exists - entering copy-on-update mode"
|
|
else
|
|
echo "Component does not exist - entering create mode"
|
|
fi
|
|
|
|
# For screens:
|
|
if [[ -f "./fluxwing/screens/${screenName}.uxm" ]]; then
|
|
echo "Screen exists - entering copy-on-update mode"
|
|
else
|
|
echo "Screen does not exist - entering create mode"
|
|
fi
|
|
```
|
|
|
|
### Step 3: Find Highest Existing Version
|
|
|
|
If original exists, find the highest versioned copy:
|
|
|
|
```bash
|
|
# Initialize
|
|
maxVersion=1 # Original is considered v1
|
|
|
|
# Find all versioned files
|
|
for file in ./fluxwing/components/${componentName}-v*.uxm; do
|
|
if [[ -f "$file" ]]; then
|
|
# Extract version number (e.g., submit-button-v3.uxm → 3)
|
|
version=$(echo "$file" | grep -oP 'v\K\d+')
|
|
if [[ $version -gt $maxVersion ]]; then
|
|
maxVersion=$version
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Calculate next version
|
|
nextVersion=$((maxVersion + 1))
|
|
newId="${componentName}-v${nextVersion}"
|
|
|
|
echo "Creating version: ${newId}"
|
|
```
|
|
|
|
**Edge case - Version gaps**: If v2 is missing but v3 and v5 exist, use MAX(versions)+1 = v6. Gaps are acceptable.
|
|
|
|
### Step 4: Load Source for Copying
|
|
|
|
Determine which file to base the new version on:
|
|
|
|
```bash
|
|
# Check if highest version exists
|
|
if [[ -f "./fluxwing/components/${componentName}-v${maxVersion}.uxm" ]]; then
|
|
# Copy from highest version (e.g., v3 if it exists)
|
|
sourceFile="${componentName}-v${maxVersion}.uxm"
|
|
else
|
|
# Copy from original (v1)
|
|
sourceFile="${componentName}.uxm"
|
|
fi
|
|
|
|
echo "Basing new version on: ${sourceFile}"
|
|
```
|
|
|
|
---
|
|
|
|
## Metadata Updates
|
|
|
|
### Required Changes in .uxm File
|
|
|
|
When creating a versioned copy, update these fields:
|
|
|
|
```json
|
|
{
|
|
"id": "submit-button-v2", // Add -v{N} suffix
|
|
"version": "1.1.0", // Increment minor version
|
|
"metadata": {
|
|
"created": "2024-10-11T12:00:00Z", // Preserve from original
|
|
"modified": "2024-10-12T15:30:00Z", // Update to current timestamp
|
|
"fidelity": "detailed" // Update if enhancing
|
|
}
|
|
}
|
|
```
|
|
|
|
### Field-by-Field Rules
|
|
|
|
| Field | Action | Example |
|
|
|-------|--------|---------|
|
|
| `id` | Add `-v{N}` suffix | `submit-button` → `submit-button-v2` |
|
|
| `version` | Increment minor | `1.0.0` → `1.1.0` → `1.2.0` |
|
|
| `metadata.created` | **Preserve** from original | `2024-10-11T12:00:00Z` (unchanged) |
|
|
| `metadata.modified` | **Update** to current time | `2024-10-12T15:30:00Z` (new) |
|
|
| `metadata.fidelity` | Update if enhancing | `sketch` → `detailed` |
|
|
| `metadata.name` | Keep same display name | `Submit Button` (unchanged) |
|
|
| `metadata.description` | Preserve or enhance | May be improved |
|
|
|
|
### Current Timestamp Format
|
|
|
|
Use ISO 8601 format:
|
|
|
|
```bash
|
|
# Generate current timestamp
|
|
currentTimestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
# Example: 2024-10-12T15:30:45Z
|
|
```
|
|
|
|
---
|
|
|
|
## User Communication
|
|
|
|
### Clear Messaging Patterns
|
|
|
|
Always inform user about versioning actions:
|
|
|
|
**✅ Good examples**:
|
|
```
|
|
"Found existing submit-button (v1.0.0). Creating submit-button-v2..."
|
|
"Created submit-button-v2 (v1.1.0) with production fidelity. Original preserved."
|
|
"Enhanced login-screen → login-screen-v2. Files created:
|
|
- ./fluxwing/screens/login-screen-v2.uxm
|
|
- ./fluxwing/screens/login-screen-v2.md
|
|
- ./fluxwing/screens/login-screen-v2.rendered.md"
|
|
```
|
|
|
|
**❌ Bad examples**:
|
|
```
|
|
"Updated submit-button" (doesn't mention versioning)
|
|
"Modified button.md" (implies in-place change)
|
|
"Created new component" (vague, no version info)
|
|
```
|
|
|
|
### Interactive Prompts
|
|
|
|
When component/screen already exists, ask user for choice:
|
|
|
|
```
|
|
Component 'submit-button' already exists (v1.0.0).
|
|
|
|
Options:
|
|
(a) Create new version (submit-button-v2)
|
|
(b) Create with different name
|
|
(c) Cancel operation
|
|
|
|
What would you like to do?
|
|
```
|
|
|
|
**Handle user responses**:
|
|
- **(a)**: Proceed with copy-on-update (create v2)
|
|
- **(b)**: Ask for new name, create with that name
|
|
- **(c)**: Cancel, no files created
|
|
|
|
---
|
|
|
|
## File System Layout
|
|
|
|
### Components Example
|
|
|
|
```
|
|
./fluxwing/components/
|
|
├── submit-button.uxm # Original (v1.0.0, fidelity=sketch)
|
|
├── submit-button.md
|
|
├── submit-button-v2.uxm # First update (v1.1.0, fidelity=detailed)
|
|
├── submit-button-v2.md
|
|
├── submit-button-v3.uxm # Second update (v1.2.0, fidelity=production)
|
|
└── submit-button-v3.md
|
|
```
|
|
|
|
### Screens Example
|
|
|
|
```
|
|
./fluxwing/screens/
|
|
├── login-screen.uxm # Original (v1.0.0)
|
|
├── login-screen.md
|
|
├── login-screen.rendered.md
|
|
├── login-screen-v2.uxm # First update (v1.1.0)
|
|
├── login-screen-v2.md
|
|
└── login-screen-v2.rendered.md
|
|
```
|
|
|
|
**Important**: Screens have THREE files per version, all with matching suffix.
|
|
|
|
---
|
|
|
|
## Edge Cases
|
|
|
|
### Edge Case 1: User Mentions .md File Only
|
|
|
|
**Request**: "Update submit-button.md to use darker colors"
|
|
|
|
**Handling**:
|
|
1. Parse component name: `submit-button`
|
|
2. Check for `submit-button.uxm` (ALWAYS start with UXM, not .md)
|
|
3. If `.uxm` exists: Enter copy-on-update mode
|
|
4. Create `submit-button-v2.uxm` AND `submit-button-v2.md`
|
|
5. Apply color changes to BOTH files
|
|
6. Inform user: "Updated both submit-button-v2.uxm and .md (UXM-first approach)"
|
|
|
|
**Why**: UXM file is source of truth. Changes to .md alone would break synchronization.
|
|
|
|
### Edge Case 2: Multiple Rapid Updates
|
|
|
|
**Requests**:
|
|
1. User: "Enhance button to detailed fidelity"
|
|
2. User: "Now enhance to production fidelity"
|
|
|
|
**Handling**:
|
|
1. First request: Create `button-v2.uxm` (detailed fidelity)
|
|
2. Second request:
|
|
- Find highest version (v2)
|
|
- Create `button-v3.uxm` (production fidelity)
|
|
- Base v3 on v2 content
|
|
|
|
**Result**: Three versions exist (original, v2, v3), each preserved.
|
|
|
|
**Note**: If user says "Add hover state" then "Add focus state", component-expander handles this with in-place updates (NO versioning).
|
|
|
|
### Edge Case 3: Version Number Gaps
|
|
|
|
**Scenario**: `button.uxm` exists, `button-v3.uxm` exists, but `button-v2.uxm` is missing
|
|
|
|
**Handling**:
|
|
1. Find ALL versions: v1 (original), v3
|
|
2. MAX(versions) = 3
|
|
3. Next version = 4
|
|
4. Create `button-v4.uxm`
|
|
|
|
**Result**: Gaps in sequence are acceptable. Always use MAX+1.
|
|
|
|
### Edge Case 4: Mixing Versioned and Original References
|
|
|
|
**Scenario**: User creates `button-v2`, then says "enhance button to production"
|
|
|
|
**Handling**:
|
|
1. "enhance button" likely refers to latest version
|
|
2. Check for highest version: v2
|
|
3. Base new version (v3) on v2
|
|
4. User can explicitly reference: "enhance button-v1 to production" if they want original
|
|
|
|
**Guideline**: Default to highest version when ambiguous.
|
|
|
|
---
|
|
|
|
## Integration with Skills
|
|
|
|
### For component-creator
|
|
|
|
**Load this module**: ✅ Yes
|
|
|
|
**When to use**:
|
|
- Before invoking designer agent
|
|
- Check if `./fluxwing/components/{component-id}.uxm` exists
|
|
- If exists: Ask user, offer copy-on-update
|
|
- If creating version: Pass base component info to agent
|
|
|
|
**Agent prompt addition**:
|
|
```
|
|
If creating versioned copy:
|
|
1. Read existing {component-id}.uxm
|
|
2. Find highest version using algorithm above
|
|
3. Generate new ID: {component-id}-v{N+1}
|
|
4. Copy content from source version
|
|
5. Apply requested changes
|
|
6. Update metadata (created, modified, version)
|
|
7. Save as {component-id}-v{N+1}.uxm and .md
|
|
```
|
|
|
|
### For screen-scaffolder
|
|
|
|
**Load this module**: ✅ Yes
|
|
|
|
**When to use**:
|
|
- Before Phase 1 (component creation)
|
|
- Check if `./fluxwing/screens/{screen-name}.uxm` exists
|
|
- If exists: Ask user, offer copy-on-update
|
|
- If creating version: Pass to composer agent
|
|
|
|
**Agent prompt addition**:
|
|
```
|
|
If creating versioned screen:
|
|
1. Read existing {screen-name}.uxm
|
|
2. Find highest version
|
|
3. Generate new ID: {screen-name}-v{N+1}
|
|
4. Create THREE files:
|
|
- {screen-name}-v{N+1}.uxm
|
|
- {screen-name}-v{N+1}.md
|
|
- {screen-name}-v{N+1}.rendered.md
|
|
5. All files use same version suffix
|
|
```
|
|
|
|
### For enhancer
|
|
|
|
**Load this module**: ✅ Yes
|
|
|
|
**When to use**:
|
|
- Always use copy-on-update for fidelity enhancements
|
|
- Each fidelity level creates new version
|
|
- Original fidelity preserved
|
|
|
|
**Agent prompt addition**:
|
|
```
|
|
When enhancing component:
|
|
1. Read current {component-id}.uxm
|
|
2. Note current fidelity level
|
|
3. Find highest version
|
|
4. Create {component-id}-v{N+1}.uxm and .md
|
|
5. Apply fidelity enhancements to new version
|
|
6. Update metadata.fidelity field
|
|
7. Update metadata.version (increment minor)
|
|
8. DO NOT overwrite original
|
|
```
|
|
|
|
### For component-expander
|
|
|
|
**Load this module**: ❌ **NO - Do not load**
|
|
|
|
**Reason**: component-expander uses in-place updates. Adding states enhances the same component ID, not creating a new version.
|
|
|
|
**Keep existing behavior**:
|
|
```
|
|
When adding states:
|
|
1. Read {component-name}.uxm
|
|
2. Modify in place (same file)
|
|
3. Add new states to behavior.states array
|
|
4. Append state sections to .md file
|
|
5. Update metadata.modified timestamp
|
|
6. Save (overwrite existing files)
|
|
```
|
|
|
|
---
|
|
|
|
## Validation Checklist
|
|
|
|
After creating versioned copy, verify:
|
|
|
|
- [ ] Filename has `-v{N}` suffix (e.g., `submit-button-v2.uxm`)
|
|
- [ ] `id` field matches filename (e.g., `"id": "submit-button-v2"`)
|
|
- [ ] `version` field incremented correctly (e.g., `1.0.0` → `1.1.0`)
|
|
- [ ] `metadata.created` preserved from original
|
|
- [ ] `metadata.modified` set to current timestamp
|
|
- [ ] Both `.uxm` and `.md` created with same version suffix
|
|
- [ ] For screens: All three files created (`.uxm`, `.md`, `.rendered.md`)
|
|
- [ ] Original files unchanged
|
|
- [ ] JSON schema validation passes
|
|
- [ ] Variables match between `.uxm` and `.md`
|
|
|
|
---
|
|
|
|
## Quick Reference
|
|
|
|
### Version Suffix Rules
|
|
|
|
| Item | Format | Example |
|
|
|------|--------|---------|
|
|
| Original filename | `{name}.uxm` | `button.uxm` |
|
|
| Original ID | `{name}` | `button` |
|
|
| Original version | `1.0.0` | `1.0.0` |
|
|
| First copy filename | `{name}-v2.uxm` | `button-v2.uxm` |
|
|
| First copy ID | `{name}-v2` | `button-v2` |
|
|
| First copy version | `1.1.0` | `1.1.0` |
|
|
| Second copy filename | `{name}-v3.uxm` | `button-v3.uxm` |
|
|
| Second copy ID | `{name}-v3` | `button-v3` |
|
|
| Second copy version | `1.2.0` | `1.2.0` |
|
|
|
|
### Skills Decision Tree
|
|
|
|
```
|
|
User requests modification
|
|
│
|
|
├─ "Add hover/focus/disabled state"
|
|
│ └─ Use component-expander
|
|
│ └─ In-place update (NO versioning)
|
|
│
|
|
├─ "Enhance to X fidelity"
|
|
│ └─ Use enhancer
|
|
│ └─ Copy-on-update (create v2)
|
|
│
|
|
├─ "Create component" (exists)
|
|
│ └─ Use component-creator
|
|
│ └─ Ask user, offer copy-on-update
|
|
│
|
|
├─ "Create screen" (exists)
|
|
│ └─ Use screen-scaffolder
|
|
│ └─ Ask user, offer copy-on-update
|
|
│
|
|
└─ "Update/modify .md file"
|
|
└─ Any skill
|
|
└─ UXM-first, then copy-on-update
|
|
```
|
|
|
|
---
|
|
|
|
## Examples
|
|
|
|
### Example 1: Enhance Component Fidelity
|
|
|
|
**User request**: "Enhance button to production fidelity"
|
|
|
|
**Current state**:
|
|
- `button.uxm` exists (v1.0.0, fidelity=sketch)
|
|
- `button.md` exists
|
|
|
|
**Process**:
|
|
1. Parse name: `button`
|
|
2. Check existence: `button.uxm` exists ✓
|
|
3. Find versions: Only original (v1) exists
|
|
4. Next version: v2
|
|
5. Create: `button-v2.uxm` and `button-v2.md`
|
|
6. Copy content from `button.uxm`
|
|
7. Update:
|
|
- `id`: `button` → `button-v2`
|
|
- `version`: `1.0.0` → `1.1.0`
|
|
- `metadata.fidelity`: `sketch` → `production`
|
|
- `metadata.modified`: current timestamp
|
|
- `metadata.created`: preserve from original
|
|
8. Apply production-level enhancements
|
|
9. Validate both files
|
|
|
|
**Result**:
|
|
- `button.uxm` unchanged (v1.0.0, sketch)
|
|
- `button-v2.uxm` created (v1.1.0, production)
|
|
- `button-v2.md` created
|
|
- User message: "Enhanced button → button-v2 (fidelity: production). Original preserved."
|
|
|
|
### Example 2: Create Existing Screen
|
|
|
|
**User request**: "Create a login screen"
|
|
|
|
**Current state**:
|
|
- `login-screen.uxm` exists (v1.0.0)
|
|
- `login-screen.md` exists
|
|
- `login-screen.rendered.md` exists
|
|
|
|
**Process**:
|
|
1. Parse name: `login-screen`
|
|
2. Check existence: `login-screen.uxm` exists ✓
|
|
3. Ask user:
|
|
```
|
|
Screen 'login-screen' already exists (v1.0.0).
|
|
|
|
Options:
|
|
(a) Create new version (login-screen-v2)
|
|
(b) Create with different name
|
|
(c) Cancel operation
|
|
|
|
What would you like to do?
|
|
```
|
|
4. User chooses: (a) Create new version
|
|
5. Find versions: Only original (v1) exists
|
|
6. Next version: v2
|
|
7. Create THREE files:
|
|
- `login-screen-v2.uxm`
|
|
- `login-screen-v2.md`
|
|
- `login-screen-v2.rendered.md`
|
|
8. Copy and update metadata
|
|
9. Apply any requested changes
|
|
|
|
**Result**:
|
|
- Original files unchanged
|
|
- Three new files created with `-v2` suffix
|
|
- User message: "Created login-screen-v2. Original login-screen preserved."
|
|
|
|
### Example 3: User Mentions .md Only
|
|
|
|
**User request**: "Update button.md to use Unicode box drawing"
|
|
|
|
**Current state**:
|
|
- `button.uxm` exists (v1.0.0)
|
|
- `button.md` exists (ASCII box drawing)
|
|
|
|
**Process**:
|
|
1. Parse name: `button` (strip .md extension)
|
|
2. **UXM-first**: Check `button.uxm` exists ✓
|
|
3. Find versions: Only original exists
|
|
4. Next version: v2
|
|
5. Create: `button-v2.uxm` AND `button-v2.md`
|
|
6. Apply Unicode box drawing to `.md`
|
|
7. Update `.uxm` metadata
|
|
|
|
**Result**:
|
|
- Both `button-v2.uxm` and `button-v2.md` created
|
|
- User message: "Updated both button-v2.uxm and .md with Unicode box drawing (UXM-first approach). Original preserved."
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Issue: Can't find highest version
|
|
|
|
**Symptom**: Multiple versions exist but algorithm fails
|
|
|
|
**Debug**:
|
|
```bash
|
|
# List all versions
|
|
ls -1 ./fluxwing/components/button*.uxm
|
|
# Should show: button.uxm, button-v2.uxm, button-v3.uxm
|
|
|
|
# Test regex extraction
|
|
for f in ./fluxwing/components/button-v*.uxm; do
|
|
echo "$f" | grep -oP 'v\K\d+'
|
|
done
|
|
# Should output: 2, 3
|
|
```
|
|
|
|
**Fix**: Ensure version suffix matches pattern `-v{digits}` exactly.
|
|
|
|
### Issue: Version number collision
|
|
|
|
**Symptom**: Two processes create same version number
|
|
|
|
**Prevention**: Always re-check highest version immediately before creating new file.
|
|
|
|
**Recovery**: If collision occurs, increment again and retry.
|
|
|
|
### Issue: Metadata.created not preserved
|
|
|
|
**Symptom**: New version has different created timestamp
|
|
|
|
**Fix**: Always read original's `metadata.created` and copy to new version.
|
|
|
|
```bash
|
|
# Extract created timestamp from original
|
|
originalCreated=$(jq -r '.metadata.created' ./fluxwing/components/button.uxm)
|
|
|
|
# Use in new version
|
|
jq --arg created "$originalCreated" '.metadata.created = $created' button-v2.uxm
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
**Copy-on-update ensures**:
|
|
- ✅ Non-destructive editing
|
|
- ✅ Component history preserved
|
|
- ✅ Clear version progression
|
|
- ✅ UXM-first consistency
|
|
- ✅ User awareness and control
|
|
|
|
**Remember**:
|
|
- Start with UXM, even if user mentions .md
|
|
- Always ask user when component/screen exists
|
|
- Increment minor version automatically
|
|
- Preserve created timestamp
|
|
- Update modified timestamp
|
|
- Create matching .uxm and .md files together
|
|
|
|
---
|
|
|
|
**End of Copy-on-Update Versioning Pattern Documentation**
|