canvas-comments

v1.0.0
localskills install xyz/canvas-comments
0 downloads
Created Jun 15, 2026
arav garg
SKILL.md

Fused canvas comments ([[comments]] in canvas.toml)

Pinned comment threads on a Fused canvas serialize as top-level [[comments]] blocks in canvas.toml. Comments with status = "open" are your work queue; resolving one is a three-field contract plus a reply, so the Workbench UI correctly attributes the work to AI.

Read: find the work

  1. Get the canvas locally: fused canvas pull <name> -o <dir> (or use the folder you have).
  2. Every [[comments]] block where status = "open" is an actionable request.
  3. anchor_udf = "<udfName>" tells you WHICH node it's about — the fix belongs in <udfName>.py (or .json/.md/.html with that stem). No anchor_udf ⇒ canvas-level comment; read content for scope.

Resolve: the full contract (all four steps, not just status)

After actually making the fix the comment asks for:

[[comments]]
id = "cmt-a"                      # never change existing ids
# ...existing fields stay untouched...
status = "resolved"               # 1. was "open"
resolved_by = "ai"                # 2. REQUIRED — this is what shows the AI badge in the UI
resolved_at = 1749600200000       # 3. ms epoch timestamp (date +%s000)

[[comments.replies]]              # 4. say what you did
id = "reply-<unique>"
content = "Changed the default to 7. Done."
created_at = 1749600200000
created_by = "ai"                 # REQUIRED on AI-written replies

Setting only status = "resolved" is wrong — without resolved_by = "ai" the UI cannot attribute the resolution, and without a reply the user doesn't know what changed.

Write: adding a new comment

[[comments]]
id = "cmt-<unique>"               # required, stable, unique in file
x = 120.0                         # required: canvas coords (floats)
y = 80.0
anchor_udf = "process_data"       # optional: node NAME (matches a [[canvas.nodes]] udfName)
offset_x = 24.0                   # only meaningful with anchor_udf
offset_y = 16.0
content = "non-empty text"        # blank content is silently dropped
status = "open"
created_by = "ai"                 # REQUIRED when you author it
created_at = 1749600000000        # ms epoch

Field reference (snake_case in TOML — never camelCase)

FieldNotes
id, content, x, yrequired; blank/whitespace content ⇒ row dropped on parse
statusopen | resolving | resolved; omitted ⇒ open
anchor_udf, offset_x, offset_yanchor by UDF name, not node id; unknown name ⇒ anchor silently dropped
created_by, resolved_by"user" | "ai"; omitted = user
created_at, updated_at, resolved_atms epoch integers
replies[[comments.replies]] with id, content, created_at, optional updated_at, created_by

Never write a TOML null/empty value — omit absent fields entirely. Comments sort by created_at on export; don't reorder blocks by hand.

Verify, then push

fused canvas validate <dir>        # structure check
fused canvas push <dir> --canvas <name>

Common mistakes

  • Resolving without resolved_by = "ai" / resolved_at / a reply (the #1 miss).
  • camelCase keys (createdAt) in TOML — the parser ignores them; data silently lost.
  • Anchoring by node id instead of anchor_udf name.
  • Marking resolved without actually making the code change the comment asks for.
  • Editing/deleting other people's ids or created_ats — append, don't rewrite history.