Canvas Work
Canvases are shared documents. Human edits fire no events, so your last view
is stale by default. Every edit is section-scoped; the wrong op duplicates
content instead of updating it.
When to use a canvas
- Large outputs go in the canvas, not chat. Any prose or code deliverable
longer than ~a dozen lines — a plan, report, spec, analysis, draft, long
diff — goes into the canvas (the right existing section, or a new one) and
the chat message shrinks to a 1-3 line summary plus a pointer ("Full plan
in the canvas under Rollout"). Chat is for coordination; canvas is for
content. Binary artifacts (charts, PDFs, images) still go via
send_file.
- Placement for a new deliverable: a genuinely new section, not stuffed
into Notes —
insert_after_section after the most related section, or
append when it belongs at the document end.
- Collaborative state lives there: task lists, decisions, running notes
that humans and agents co-edit.
- In a DM with no canvas, a long deliverable may stay a chat message — but
where a canvas exists, prefer it (there is no canvas-create tool; "new
structure" means a new section on an existing canvas).
The loop
- Read first.
canvas_read before editing — never edit from memory or
a previous turn's read. One fresh read covers all the edits you compose
in the same turn. Read-backs truncate at ~8000 chars with an explicit
…[truncated at N chars] marker: if you see it, the canvas continues
below what you read — never conclude a section is absent from a
truncated read (heading-anchored edits still resolve against the full
canvas host-side, so editing below the cut is safe).
- Locate the target in the read-back. For section-wide edits,
section_text is the exact heading text — a body-text anchor silently
degrades a section rewrite into a single-block edit that corrupts the
section. For checklist items, a body-text anchor (the item's exact
text) is the CORRECT, deliberate way to target that one item (see
Checklists).
- Pick the op:
replace_section — the content EXISTS and you are changing it (ticking
a box, updating status, rewriting a paragraph). Updates always use this.
insert_after_section — a genuinely NEW section, one whose heading
appears nowhere in the canvas, placed after a specific section.
append — content for the document END: log entries, list additions,
or a new final section (markdown starting with its new heading).
- Never create a heading that already exists — anywhere in your
markdown, not just the first line. A best-effort host guard refuses
obvious duplicate-heading inserts (the refusal wakes you with a note),
but it is narrow — the rule is yours to enforce, the guard is not a
backstop. Note: appending log entries whose markdown repeats the log
section's heading is the classic way to trip it — append entry lines
only, no heading.
- Batch small edits: one
canvas_update per section with the complete
new section markdown, not a burst of tiny ops.
- Verify structural changes. A FAILED edit wakes you with a system
note saying why — fix from a fresh read. A success note only rides along
on your next wake, so after adding, reordering, or rewriting sections,
canvas_read and confirm the result before reporting done. Never report
a structural edit as landed that you haven't re-read.
Checklists: item-level ops only
Tick state is UI-only — a hard platform limit, live-probed: - [x] in
edit markdown is silently ignored (items always land unticked), and any
section rewrite RESETS every ticked box (the host warns you when that
happens). So on checklists:
- Add an item:
insert_after_section on the section heading, markdown
= just the new - [ ] item line. Existing items and their ticks stay
intact. Never re-send the whole list to add one item.
- Mark an item done: you cannot tick its box. Edit the ITEM itself —
section_text = the item's exact text (a non-heading match targets that
single item), op replace_section, markdown like
- ✅ deploy the site (done). Humans tick boxes; agents mark done in
the item text.
- Remove or reword an item: same item-level targeting.
- Rewrite the whole section only when resetting it is the point (e.g.
replacing placeholder content) — every ticked box comes back unticked.
Worked example: mark a task done
User: "mark the deploy task done."
canvas_read → Tasks contains - [ ] deploy the site and
- [ ] write the announcement.
Item-level edit — target the item, not the section:
canvas_update op=replace_section section_text="deploy the site" markdown:
- ✅ deploy the site (done)
A full-section rewrite here would land, but with every checkbox reset
and no way to express the tick.
For NON-list sections, replace_section on the heading swaps the whole
section region — send the complete section markdown, heading included.
Lines you omit are deleted.
Section model
- A section region is a heading plus every block down to the NEXT heading
(canvases have three heading levels; any of them ends the region).
Sub-headings end the region:
replace_section on ## Tasks
when it contains ### Sprint 1 replaces only the blocks above the ###
and leaves every subsection in place — resending subsection markdown
duplicates it below the new content. Edit the parent's intro and each
subsection as separate ops.
- Always include the heading line in
replace_section markdown. The
heading block itself gets replaced: omit it and the section visually
merges into the one above and can never be heading-targeted again.
- These region semantics apply only when
section_text matches a heading.
A body-text match targets ONE block — deliberate and correct for
checklist items, silently corrupting for section rewrites. Know which
edit you are making.
insert_after_section inserts after the region's last block, not directly
after the heading.
Pitfalls
- Heading matching: case-insensitive, trimmed; exact match beats contains;
contains ties go to the FIRST matching heading in document order (
Task
hits Tasks before Task archive). Use the full exact heading.
- Section ids are ephemeral; the host re-resolves them on every edit.
Heading text from a fresh read is the only reliable addressing.
canvas_update is fire-and-forget: failures wake you with a system note
(fix from a fresh read); success notes arrive silently on your next wake.
For anything structural, canvas_read is the confirmation.
- Partial replace: if a read-back (or a failure note) shows your new
content in place with old blocks of the same section still sitting below
it, re-issue the SAME
replace_section on the same heading — the
leftovers are now that region's body, so the identical retry is the
idempotent fix. If the retry leaves the SAME leftovers again, those
blocks are unaddressable host-side — stop retrying and tell the human
(they can delete them in the canvas UI). For anything else that looks
wrong, re-read and compose a fresh edit.
canvas_read's timeout is in seconds (default 20). A timeout error can
fire even though the host read succeeded — retry, optionally with a
larger timeout.
1---2name: canvas-work3description: Edit Slack canvases without corrupting them — read-before-write, section targeting by heading, replace vs insert discipline. Use EVERY time you edit a canvas with canvas_update (room canvases, task lists, deliverable docs), not just the first time.4---56# Canvas Work78Canvases are shared documents. Human edits fire no events, so your last view9is stale by default. Every edit is section-scoped; the wrong op duplicates10content instead of updating it.1112## When to use a canvas1314- **Large outputs go in the canvas, not chat.** Any prose or code deliverable15 longer than ~a dozen lines — a plan, report, spec, analysis, draft, long16 diff — goes into the canvas (the right existing section, or a new one) and17 the chat message shrinks to a 1-3 line summary plus a pointer ("Full plan18 in the canvas under Rollout"). Chat is for coordination; canvas is for19 content. Binary artifacts (charts, PDFs, images) still go via `send_file`.20- **Placement for a new deliverable**: a genuinely new section, not stuffed21 into Notes — `insert_after_section` after the most related section, or22 `append` when it belongs at the document end.23- **Collaborative state lives there**: task lists, decisions, running notes24 that humans and agents co-edit.25- In a DM with no canvas, a long deliverable may stay a chat message — but26 where a canvas exists, prefer it (there is no canvas-create tool; "new27 structure" means a new section on an existing canvas).2829## The loop30311. **Read first.** `canvas_read` before editing — never edit from memory or32 a previous turn's read. One fresh read covers all the edits you compose33 in the same turn. Read-backs truncate at ~8000 chars with an explicit34 `…[truncated at N chars]` marker: if you see it, the canvas continues35 below what you read — never conclude a section is absent from a36 truncated read (heading-anchored edits still resolve against the full37 canvas host-side, so editing below the cut is safe).382. **Locate the target** in the read-back. For section-wide edits,39 `section_text` is the exact heading text — a body-text anchor silently40 degrades a section rewrite into a single-block edit that corrupts the41 section. For checklist items, a body-text anchor (the item's exact42 text) is the CORRECT, deliberate way to target that one item (see43 Checklists).443. **Pick the op:**45 - `replace_section` — the content EXISTS and you are changing it (ticking46 a box, updating status, rewriting a paragraph). Updates always use this.47 - `insert_after_section` — a genuinely NEW section, one whose heading48 appears nowhere in the canvas, placed after a specific section.49 - `append` — content for the document END: log entries, list additions,50 or a new final section (markdown starting with its new heading).514. **Never create a heading that already exists** — anywhere in your52 markdown, not just the first line. A best-effort host guard refuses53 obvious duplicate-heading inserts (the refusal wakes you with a note),54 but it is narrow — the rule is yours to enforce, the guard is not a55 backstop. Note: appending log entries whose markdown repeats the log56 section's heading is the classic way to trip it — append entry lines57 only, no heading.585. **Batch small edits**: one `canvas_update` per section with the complete59 new section markdown, not a burst of tiny ops.606. **Verify structural changes.** A FAILED edit wakes you with a system61 note saying why — fix from a fresh read. A success note only rides along62 on your next wake, so after adding, reordering, or rewriting sections,63 `canvas_read` and confirm the result before reporting done. Never report64 a structural edit as landed that you haven't re-read.6566## Checklists: item-level ops only6768Tick state is **UI-only** — a hard platform limit, live-probed: `- [x]` in69edit markdown is silently ignored (items always land unticked), and any70section rewrite RESETS every ticked box (the host warns you when that71happens). So on checklists:7273- **Add an item**: `insert_after_section` on the section heading, markdown74 = just the new `- [ ] item` line. Existing items and their ticks stay75 intact. Never re-send the whole list to add one item.76- **Mark an item done**: you cannot tick its box. Edit the ITEM itself —77 `section_text` = the item's exact text (a non-heading match targets that78 single item), op `replace_section`, markdown like79 `- ✅ deploy the site (done)`. Humans tick boxes; agents mark done in80 the item text.81- **Remove or reword an item**: same item-level targeting.82- **Rewrite the whole section only when resetting it is the point** (e.g.83 replacing placeholder content) — every ticked box comes back unticked.8485### Worked example: mark a task done8687User: "mark the deploy task done."88891. `canvas_read` → Tasks contains `- [ ] deploy the site` and90 `- [ ] write the announcement`.912. Item-level edit — target the item, not the section:9293 ```94 canvas_update op=replace_section section_text="deploy the site" markdown:95 - ✅ deploy the site (done)96 ```9798 A full-section rewrite here would land, but with every checkbox reset99 and no way to express the tick.100101For NON-list sections, `replace_section` on the heading swaps the whole102section region — send the complete section markdown, heading included.103Lines you omit are deleted.104105## Section model106107- A section region is a heading plus every block down to the NEXT heading108 (canvases have three heading levels; any of them ends the region).109 Sub-headings end the region: `replace_section` on `## Tasks`110 when it contains `### Sprint 1` replaces only the blocks above the `###`111 and leaves every subsection in place — resending subsection markdown112 duplicates it below the new content. Edit the parent's intro and each113 subsection as separate ops.114- **Always include the heading line** in `replace_section` markdown. The115 heading block itself gets replaced: omit it and the section visually116 merges into the one above and can never be heading-targeted again.117- These region semantics apply only when `section_text` matches a heading.118 A body-text match targets ONE block — deliberate and correct for119 checklist items, silently corrupting for section rewrites. Know which120 edit you are making.121- `insert_after_section` inserts after the region's last block, not directly122 after the heading.123124## Pitfalls125126- Heading matching: case-insensitive, trimmed; exact match beats contains;127 contains ties go to the FIRST matching heading in document order (`Task`128 hits `Tasks` before `Task archive`). Use the full exact heading.129- Section ids are ephemeral; the host re-resolves them on every edit.130 Heading text from a fresh read is the only reliable addressing.131- `canvas_update` is fire-and-forget: failures wake you with a system note132 (fix from a fresh read); success notes arrive silently on your next wake.133 For anything structural, `canvas_read` is the confirmation.134- **Partial replace**: if a read-back (or a failure note) shows your new135 content in place with old blocks of the same section still sitting below136 it, re-issue the SAME `replace_section` on the same heading — the137 leftovers are now that region's body, so the identical retry is the138 idempotent fix. If the retry leaves the SAME leftovers again, those139 blocks are unaddressable host-side — stop retrying and tell the human140 (they can delete them in the canvas UI). For anything else that looks141 wrong, re-read and compose a fresh edit.142- `canvas_read`'s `timeout` is in seconds (default 20). A timeout error can143 fire even though the host read succeeded — retry, optionally with a144 larger timeout.