- Rename skill/ to skills/ for consistency with naming conventions - Rename agent/ to agents/ and command/ to commands/ - Update AGENTS.md with all directory references - Update scripts/test-skill.sh paths - Update prompts/athena.txt documentation This aligns with best practices of using plural directory names and updates all documentation to reflect the new structure.
183 lines
5.2 KiB
Markdown
183 lines
5.2 KiB
Markdown
# Validation Reference
|
|
|
|
Checklists, validation algorithms, and common bug fixes.
|
|
|
|
---
|
|
|
|
## Pre-Flight Validation Algorithm
|
|
|
|
Run BEFORE writing the file:
|
|
|
|
```
|
|
FUNCTION validateDiagram(elements):
|
|
errors = []
|
|
|
|
// 1. Validate shape-text bindings
|
|
FOR each shape IN elements WHERE shape.boundElements != null:
|
|
FOR each binding IN shape.boundElements:
|
|
textElement = findById(elements, binding.id)
|
|
IF textElement == null:
|
|
errors.append("Shape {shape.id} references missing text {binding.id}")
|
|
ELSE IF textElement.containerId != shape.id:
|
|
errors.append("Text containerId doesn't match shape")
|
|
|
|
// 2. Validate arrow connections
|
|
FOR each arrow IN elements WHERE arrow.type == "arrow":
|
|
sourceShape = findShapeNear(elements, arrow.x, arrow.y)
|
|
IF sourceShape == null:
|
|
errors.append("Arrow {arrow.id} doesn't start from shape edge")
|
|
|
|
finalPoint = arrow.points[arrow.points.length - 1]
|
|
endX = arrow.x + finalPoint[0]
|
|
endY = arrow.y + finalPoint[1]
|
|
targetShape = findShapeNear(elements, endX, endY)
|
|
IF targetShape == null:
|
|
errors.append("Arrow {arrow.id} doesn't end at shape edge")
|
|
|
|
IF arrow.points.length > 2:
|
|
IF arrow.elbowed != true:
|
|
errors.append("Arrow {arrow.id} missing elbowed:true")
|
|
IF arrow.roundness != null:
|
|
errors.append("Arrow {arrow.id} should have roundness:null")
|
|
|
|
// 3. Validate unique IDs
|
|
ids = [el.id for el in elements]
|
|
duplicates = findDuplicates(ids)
|
|
IF duplicates.length > 0:
|
|
errors.append("Duplicate IDs: {duplicates}")
|
|
|
|
// 4. Validate bounding boxes
|
|
FOR each arrow IN elements WHERE arrow.type == "arrow":
|
|
maxX = max(abs(p[0]) for p in arrow.points)
|
|
maxY = max(abs(p[1]) for p in arrow.points)
|
|
IF arrow.width < maxX OR arrow.height < maxY:
|
|
errors.append("Arrow {arrow.id} bounding box too small")
|
|
|
|
RETURN errors
|
|
|
|
FUNCTION findShapeNear(elements, x, y, tolerance=15):
|
|
FOR each shape IN elements WHERE shape.type IN ["rectangle", "ellipse"]:
|
|
edges = [
|
|
(shape.x + shape.width/2, shape.y), // top
|
|
(shape.x + shape.width/2, shape.y + shape.height), // bottom
|
|
(shape.x, shape.y + shape.height/2), // left
|
|
(shape.x + shape.width, shape.y + shape.height/2) // right
|
|
]
|
|
FOR each edge IN edges:
|
|
IF abs(edge.x - x) < tolerance AND abs(edge.y - y) < tolerance:
|
|
RETURN shape
|
|
RETURN null
|
|
```
|
|
|
|
---
|
|
|
|
## Checklists
|
|
|
|
### Before Generating
|
|
|
|
- [ ] Identified all components from codebase
|
|
- [ ] Mapped all connections/data flows
|
|
- [ ] Chose layout pattern (vertical, horizontal, hub-and-spoke)
|
|
- [ ] Selected color palette (default, AWS, Azure, K8s)
|
|
- [ ] Planned grid positions
|
|
- [ ] Created ID naming scheme
|
|
|
|
### During Generation
|
|
|
|
- [ ] Every labeled shape has BOTH shape AND text elements
|
|
- [ ] Shape has `boundElements: [{ "type": "text", "id": "{id}-text" }]`
|
|
- [ ] Text has `containerId: "{shape-id}"`
|
|
- [ ] Multi-point arrows have `elbowed: true`, `roundness: null`, `roughness: 0`
|
|
- [ ] Arrows have `startBinding` and `endBinding`
|
|
- [ ] No diamond shapes used
|
|
- [ ] Applied staggering formula for multiple arrows
|
|
|
|
### Arrow Validation (Every Arrow)
|
|
|
|
- [ ] Arrow `x,y` calculated from shape edge
|
|
- [ ] Final point offset = `targetEdge - sourceEdge`
|
|
- [ ] Arrow `width` = `max(abs(point[0]))`
|
|
- [ ] Arrow `height` = `max(abs(point[1]))`
|
|
- [ ] U-turn arrows have 40-60px clearance
|
|
|
|
### After Generation
|
|
|
|
- [ ] All `boundElements` IDs reference valid text elements
|
|
- [ ] All `containerId` values reference valid shapes
|
|
- [ ] All arrows start within 15px of shape edge
|
|
- [ ] All arrows end within 15px of shape edge
|
|
- [ ] No duplicate IDs
|
|
- [ ] Arrow bounding boxes match points
|
|
- [ ] File is valid JSON
|
|
|
|
---
|
|
|
|
## Common Bugs and Fixes
|
|
|
|
### Bug: Arrow appears disconnected/floating
|
|
|
|
**Cause**: Arrow `x,y` not calculated from shape edge.
|
|
|
|
**Fix**:
|
|
```
|
|
Rectangle bottom: arrow_x = shape.x + shape.width/2
|
|
arrow_y = shape.y + shape.height
|
|
```
|
|
|
|
### Bug: Arrow endpoint doesn't reach target
|
|
|
|
**Cause**: Final point offset calculated incorrectly.
|
|
|
|
**Fix**:
|
|
```
|
|
target_edge = (target.x + target.width/2, target.y)
|
|
offset_x = target_edge.x - arrow.x
|
|
offset_y = target_edge.y - arrow.y
|
|
Final point = [offset_x, offset_y]
|
|
```
|
|
|
|
### Bug: Multiple arrows from same source overlap
|
|
|
|
**Cause**: All arrows start from identical `x,y`.
|
|
|
|
**Fix**: Stagger start positions:
|
|
```
|
|
For 5 arrows from bottom edge:
|
|
arrow1.x = shape.x + shape.width * 0.2
|
|
arrow2.x = shape.x + shape.width * 0.35
|
|
arrow3.x = shape.x + shape.width * 0.5
|
|
arrow4.x = shape.x + shape.width * 0.65
|
|
arrow5.x = shape.x + shape.width * 0.8
|
|
```
|
|
|
|
### Bug: Callback arrow doesn't loop correctly
|
|
|
|
**Cause**: U-turn path lacks clearance.
|
|
|
|
**Fix**: Use 4-point path:
|
|
```
|
|
Points = [[0, 0], [clearance, 0], [clearance, -vert], [final_x, -vert]]
|
|
clearance = 40-60px
|
|
```
|
|
|
|
### Bug: Labels don't appear inside shapes
|
|
|
|
**Cause**: Using `label` property instead of separate text element.
|
|
|
|
**Fix**: Create TWO elements:
|
|
1. Shape with `boundElements` referencing text
|
|
2. Text with `containerId` referencing shape
|
|
|
|
### Bug: Arrows are curved, not 90-degree
|
|
|
|
**Cause**: Missing elbow properties.
|
|
|
|
**Fix**: Add all three:
|
|
```json
|
|
{
|
|
"roughness": 0,
|
|
"roundness": null,
|
|
"elbowed": true
|
|
}
|
|
```
|