Skills for working with Obsidian vaults and related formats: Obsidian Flavored Markdown, JSON Canvas files, the Obsidian CLI, and Defuddle for clean web content extraction.
96
96%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advisory
Suggest reviewing before use
\n escape in JSON stringsNEVER embed literal newline characters or double-escaped \\n sequences inside JSON string values. JSON strings must use the escape sequence \n for line breaks.
WHY: Literal newlines produce unparseable JSON that silently corrupts the canvas file, while \\n renders as visible backslash-n characters on screen instead of the intended line break.
Bad — literal newline (invalid JSON):
{
"id": "6f0ad84f44ce9c17",
"type": "text",
"text": "Line one
Line two"
}Bad — double-escaped (renders as literal \n on screen):
{
"id": "6f0ad84f44ce9c17",
"type": "text",
"text": "Line one\\nLine two"
}Good — single JSON escape:
{
"id": "6f0ad84f44ce9c17",
"type": "text",
"text": "Line one\nLine two"
}NEVER reuse the same id value for more than one node or edge. Every id must be unique across all nodes and edges.
WHY: Duplicate IDs cause one element to silently overwrite another, resulting in missing nodes or edges with no error reported by Obsidian.
Bad:
{
"nodes": [
{ "id": "aabbccdd11223344", "type": "text", "x": 0, "y": 0, "width": 200, "height": 100, "text": "Node A" },
{ "id": "aabbccdd11223344", "type": "text", "x": 300, "y": 0, "width": 200, "height": 100, "text": "Node B" }
]
}Good — each ID is a distinct 16-char hex value:
{
"nodes": [
{ "id": "aabbccdd11223344", "type": "text", "x": 0, "y": 0, "width": 200, "height": 100, "text": "Node A" },
{ "id": "11223344aabbccdd", "type": "text", "x": 300, "y": 0, "width": 200, "height": 100, "text": "Node B" }
]
}NEVER set fromNode or toNode to an ID that does not exist in the nodes array.
WHY: Dangling edge references produce invisible edges with no error, making it impossible to diagnose missing connections just by viewing the canvas.
Bad — toNode references a missing ID:
{
"nodes": [
{ "id": "aabbccdd11223344", "type": "text", "x": 0, "y": 0, "width": 200, "height": 100, "text": "Start" }
],
"edges": [
{ "id": "edge000000000001", "fromNode": "aabbccdd11223344", "toNode": "doesnotexist1234" }
]
}Good — toNode resolves to an existing node:
{
"nodes": [
{ "id": "aabbccdd11223344", "type": "text", "x": 0, "y": 0, "width": 200, "height": 100, "text": "Start" },
{ "id": "11223344aabbccdd", "type": "text", "x": 300, "y": 0, "width": 200, "height": 100, "text": "End" }
],
"edges": [
{ "id": "edge000000000001", "fromNode": "aabbccdd11223344", "toNode": "11223344aabbccdd" }
]
}NEVER place a child node at coordinates outside the group's x/y/width/height rectangle.
WHY: Nodes outside the group bounds appear as free-floating canvas items, breaking the visual grouping.
Bad — child at x=1200 is outside group that ends at x=1000:
{
"nodes": [
{ "id": "group00000000001", "type": "group", "x": 200, "y": 100, "width": 800, "height": 500, "label": "My Group" },
{ "id": "child00000000001", "type": "text", "x": 1200, "y": 150, "width": 200, "height": 80, "text": "Orphaned child" }
]
}Good — child fits inside group bounds (x=200..1000, y=100..600):
{
"nodes": [
{ "id": "group00000000001", "type": "group", "x": 200, "y": 100, "width": 800, "height": 500, "label": "My Group" },
{ "id": "child00000000001", "type": "text", "x": 250, "y": 150, "width": 200, "height": 80, "text": "Inside group" }
]
}fromEnd/toEnd to unsupported valuesNEVER set fromEnd or toEnd to any value other than "none" or "arrow".
WHY: Unsupported end-marker values are either silently discarded or cause the edge to render incorrectly, with no validation error.
Bad:
{ "id": "edge000000000001", "fromNode": "aabbccdd11223344", "toNode": "11223344aabbccdd", "fromEnd": "circle", "toEnd": "diamond" }Good:
{ "id": "edge000000000001", "fromNode": "aabbccdd11223344", "toNode": "11223344aabbccdd", "fromEnd": "none", "toEnd": "arrow" }NEVER supply a bare integer for the color field. The canvasColor type is always a JSON string.
WHY: Canvas implementations that enforce strict typing will reject the file or ignore the color when an integer is provided instead of a quoted string.
Bad — integer value: "color": 1
Good — string value: "color": "1"
defuddle
json-canvas
evals
obsidian-bases
evals
references
obsidian-cli
evals
obsidian-markdown
evals