@creator-notes/cnotes 0.26.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,26 @@ metadata:
9
9
 
10
10
  A command-line interface for CreatorNotes — create notes, build canvases, search knowledge, and manage workspaces from the terminal.
11
11
 
12
+ ## Writing quality — the humanizer (read the reference before authoring prose)
13
+
14
+ Whenever a task **creates or edits audience-facing prose** that will be saved to
15
+ CreatorNotes — a note body, a pre-read, a strategy or workshop note, a public
16
+ draft, a Slack message, or a canvas orientation banner — **read
17
+ [`references/humanizer.md`](references/humanizer.md) first**, then draft, audit,
18
+ and save through it. It removes generic model habits (the kill-list, fake
19
+ contrasts, forced triads) while a preservation gate holds every number, date, ID,
20
+ link, and mention title fixed, so improving prose never corrupts meaning.
21
+
22
+ **Skip it** — do not humanize — for raw capture and structured data: transcripts,
23
+ voice memos, customer quotes, source evidence, schemas, tables, and metric-heavy
24
+ reference material. Store those unchanged and humanize only the derived artifact.
25
+
26
+ **The user's words override.** "Save this as raw capture", "audit only, change
27
+ nothing", "preserve timings and links", "use the neutral / company voice", and
28
+ "rewrite in my personal voice" each select a specific mode described in the
29
+ reference. Loading the reference costs no extra context on non-writing tasks —
30
+ pull it in only when the work is prose.
31
+
12
32
  ## Getting Started
13
33
 
14
34
  ### Install
@@ -199,8 +219,12 @@ cnotes canvas get <canvasId>
199
219
  # reason about occupancy in one read instead of unioning the per-kind arrays.
200
220
  # (You rarely need this for placement — `cnotes canvas place` already avoids overlaps.)
201
221
 
202
- # Read every note on a canvas as one concatenated markdown document, in display
203
- # order (top-to-bottom, then left-to-right) — think across a canvas in one round-trip
222
+ # Read a canvas as STRUCTURE, not just content — one round-trip to think across it.
223
+ # Returns the notes as concatenated markdown in display order (top-to-bottom, then
224
+ # left-to-right) GROUPED UNDER THEIR SECTION FRAME, plus the edges (the argument)
225
+ # and the portals (the navigation). --json gives { canvas, sections, elements,
226
+ # edges, portals }; every element carries the `section` it sits inside.
227
+ # You should not need a second `canvas get` just to see the frames or the wiring.
204
228
  cnotes canvas read <canvasId>
205
229
 
206
230
  # AI digest of canvas: narrative summary + per-note summary list for every note on the canvas
@@ -218,23 +242,25 @@ cnotes canvas archive <canvasId>
218
242
  cnotes canvas unarchive <canvasId>
219
243
 
220
244
  # Add nodes to canvas — see "Canvas Elements — When to Use What" before choosing.
221
- # Default to NOTES for content; text/richtext/list are presentation furniture.
245
+ # Default to NOTES for content; text/list/section are presentation furniture.
222
246
  cnotes canvas add-node <canvasId> --note <noteId> [--x <n>] [--y <n>] # (despite the name, add-node adds NOTE nodes only)
223
- # text = one-line section labels / wayfinding only (title case, never ALL CAPS)
224
- cnotes canvas add-text <canvasId> --text "<content>" [--size heading|paragraph] [--x <n>] [--y <n>]
225
- # richtext = STICKY-sized framing (at most ~3 sentences): orientation banners, emphasis
247
+ # text = free-form rich text placed on the canvas (wire type: richtext). Markdown —
248
+ # headings, emphasis, images. Optional background tint (--color); DEFAULT IS NO BACKGROUND.
249
+ # A HEADING IS JUST `--content '# Q1 Goals'` with no tint — there is no separate heading
250
+ # element. CARD-sized framing (at most ~3 sentences): orientation banners, emphasis
226
251
  # quoting a note, image tiles. Unversioned, unsearchable, no display ID (nothing can cite
227
252
  # it) — never the sole home of a claim. NO [NOTE-X](relationship:...) / [[...]] mention
228
253
  # syntax (corrupts the card; use plain display IDs). Knowledge worth keeping = a typed note.
229
- cnotes canvas add-richtext <canvasId> --content-file <path.md> [--size small|medium|large] [--color "<hex>"] [--x <n>] [--y <n>]
230
- cnotes canvas add-richtext <canvasId> --content "<content>" [--size small|medium|large] [--color "<hex>"] [--x <n>] [--y <n>] # inline (short only)
254
+ cnotes canvas add-text <canvasId> --content "<markdown>" [--size small|medium|large] [--color "<hex>"] [--x <n>] [--y <n>] # inline (short only)
255
+ cnotes canvas add-text <canvasId> --content-file <path.md> [--size small|medium|large] [--color "<hex>"] [--x <n>] [--y <n>]
256
+ cnotes canvas add-richtext ... # DEPRECATED alias of add-text (identical flags). Use add-text.
231
257
  # A list item is a COLLECTION OF NOTES, not a text block. --description is a one-line FRAME;
232
258
  # the content is the member notes you pass to --notes. If you have N distinct things
233
259
  # (questions, risks, options), create N notes first — pick the right type, e.g. a Question
234
260
  # note per open question — then group them: --notes ID1,ID2,ID3. Pasting a numbered/bulleted
235
261
  # list into --description with no --notes is the wrong shape (you get a "0 items" container
236
- # masquerading as prose). For a short sticky-sized framing blurb (at most ~3 sentences, no
237
- # member notes) use add-richtext; a multi-item markdown block is N typed notes, never richtext
262
+ # masquerading as prose). For a short card-sized framing blurb (at most ~3 sentences, no
263
+ # member notes) use add-text; a multi-item markdown block is N typed notes, never a text element
238
264
  # (see "Canvas Elements — When to Use What").
239
265
  cnotes canvas add-list <canvasId> --description "<markdown>" [--notes <id1,id2,...>] [--view list|grid] [--x <n>] [--y <n>]
240
266
  cnotes canvas add-link <canvasId> --target <otherCanvasId> [--x <n>] [--y <n>]
@@ -248,6 +274,13 @@ cnotes canvas add-link <canvasId> --target <otherCanvasId> [--x <n>] [--y <n>]
248
274
  cnotes canvas add-section <canvasId> --name "<title>" --notes <id1,id2,...> [--columns <n>] [--color "<hex>"]
249
275
  cnotes canvas add-section <canvasId> --name "<title>" [--x <n>] [--y <n>] [--width <n>] [--height <n>] # empty frame
250
276
 
277
+ # Resize / rename / move / recolor an EXISTING frame, IN PLACE. Takes the SECTION-<n>
278
+ # display id. ALWAYS use this to grow a frame around new content — deleting and
279
+ # re-adding a section mints a NEW id (SECTION-51 becomes SECTION-57) and silently
280
+ # breaks every [[SECTION-51]] mention pointing at the old one.
281
+ cnotes canvas update-section <canvasId> --section SECTION-51 --height 1800
282
+ cnotes canvas update-section <canvasId> --section SECTION-51 --name "Open Questions" [--x <n>] [--y <n>] [--width <n>] [--color "<hex>"] [--clear-color]
283
+
251
284
  # Place items using a declarative layout (NO x/y math — server packs them)
252
285
  # This is the PREFERRED way to add multiple items. See "Canvas Layout" below.
253
286
  cnotes canvas place <canvasId> --spec ./layout.json
@@ -258,22 +291,27 @@ cnotes canvas place <canvasId> --spec-inline '<short JSON>' # for tiny specs on
258
291
  cnotes canvas bulk-add <canvasId> --notes '[{"noteId":"NOTE-1","x":100,"y":100},{"noteId":"NOTE-2","x":400,"y":100}]'
259
292
 
260
293
  # Move / remove nodes (--type for non-note nodes, e.g. --type section)
261
- cnotes canvas move-node <canvasId> --node <nodeId> --x <n> --y <n> [--type note|text|list|canvas|richtext|section]
294
+ # NOTE: `text` in these type lists is the LEGACY heading kind (old canvases only). It can no
295
+ # longer be CREATED, but existing ones stay movable and deletable — that is why it is still
296
+ # accepted here. Today's Text element is `richtext`.
297
+ cnotes canvas move-node <canvasId> --node <nodeId> --x <n> --y <n> [--type note|richtext|list|canvas|section|text]
262
298
  cnotes canvas remove-node <canvasId> --node <nodeId>
263
299
 
264
300
  # Bulk remove multiple nodes at once
265
301
  cnotes canvas bulk-remove <canvasId> --nodes '["nodeId1","nodeId2"]'
266
302
 
267
303
  # Bulk move multiple nodes at once (more efficient than repeated move-node)
268
- cnotes canvas bulk-move <canvasId> --moves '[{"nodeId":"abc","nodeType":"note","x":200,"y":300},{"nodeId":"def","nodeType":"text","x":500,"y":300}]'
269
- # nodeType: note | text | list | canvas | richtext (defaults to "note")
304
+ cnotes canvas bulk-move <canvasId> --moves '[{"nodeId":"abc","nodeType":"note","x":200,"y":300},{"nodeId":"def","nodeType":"richtext","x":500,"y":300}]'
305
+ # nodeType: note | richtext | list | canvas | section | text (defaults to "note"; `text` = legacy)
270
306
 
271
- # Connect nodes — edges can join ANY node type (note, text, list, richtext, canvas-link),
307
+ # Connect nodes — edges can join ANY node type (note, richtext, list, canvas-link, legacy text),
272
308
  # as long as both endpoints live on the same canvas.
273
- # Labels are short verbs ("fixes", "grounds", "then"). Edges are visual-only and `canvas read`
274
- # drops them, so when an edge encodes a relationship between NOTES that a reading agent must
275
- # see, ALSO mirror it as a relationship mention in the note bodies. Edges to text/richtext/
276
- # canvas-link nodes are diagram decoration (labels, portals) and need no mention.
309
+ # Labels are short verbs ("fixes", "grounds", "then"). `canvas read` DOES return edges now,
310
+ # but an edge is still CANVAS-LOCAL: it is not part of the note graph, so it never shows up in
311
+ # `rel list`, is not searchable, and means nothing away from this canvas. So when an edge
312
+ # encodes a relationship between NOTES, ALSO mirror it as a relationship mention in the note
313
+ # bodies — that is the copy that travels. Edges to text/list/canvas-link nodes are diagram
314
+ # decoration (labels, portals) and need no mention.
277
315
  cnotes canvas add-edge <canvasId> --source <nodeId> --target <nodeId> [--label "<text>"]
278
316
 
279
317
  # Templates — create structured canvases from predefined layouts
@@ -337,19 +375,47 @@ cnotes timeline -q "auth decision" --since 30d # search within a window
337
375
  # incoming → notes that link TO this note (backlinks — "what cites me")
338
376
  # outgoing → notes this note links to ("what I cite")
339
377
  # Each row carries the related note inline so you rarely need a follow-up fetch:
340
- # { direction, isStale, madeAgainstVersion,
378
+ # { id, direction, origin, live, isStale, madeAgainstVersion, relatedNoteCurrentVersion,
341
379
  # relatedNote: { displayId, title, type, workspaceName },
342
380
  # relationshipType: { name, label } }
343
- # `isStale: true` = the related note has changed since the link was made.
344
381
  cnotes rel list --note <displayId>
345
382
  cnotes rel list --note INSIGHT-1 --json | jq '.incoming[].relatedNote.displayId'
346
383
 
384
+ # By default you see links that hold RIGHT NOW. --history adds the superseded ones.
385
+ cnotes rel list --note <displayId> --history
386
+ cnotes rel list [--type implements] [--limit <n>] # workspace-wide
387
+
388
+ # Delete a relationship by id (ids come from `rel list`).
389
+ cnotes rel rm <relationshipId>
390
+
347
391
  # List the relationship types defined in the workspace. Check this BEFORE
348
392
  # using a relationship:<type> label so you reuse an existing type instead of
349
393
  # creating a near-duplicate.
350
394
  cnotes rel types
351
395
  ```
352
396
 
397
+ **Read the two provenance fields before you trust a link.** They answer different
398
+ questions, and conflating them is the classic mistake:
399
+
400
+ | Field | Meaning |
401
+ |---|---|
402
+ | `origin: "content"` | Extracted from a `[[type::TARGET]]` chip in the note body. Its lifecycle IS the body's — delete the chip and the link stops being live. |
403
+ | `origin: "declared"` | Not attributable to a chip in the current body: a structural link (from a duplicate/move), one created explicitly, **or an old link predating chip tracking** (see caveat below). Editing the body never retires it, so a declared link that no longer holds can ONLY be removed with `rel rm`. |
404
+ | `live: false` | A content chip that is **gone from the note's current version**. This is the obsolete one. Hidden unless you pass `--history`. |
405
+ | `isStale: true` | **Citation drift, NOT obsolescence.** The link holds; the note it points at has simply advanced since it was cited (`madeAgainstVersion` < `relatedNoteCurrentVersion`). Often the most interesting signal in the graph — a claim resting on a source that moved. Worth re-reading, never worth auto-deleting. |
406
+
407
+ Relationships are append-only: every save re-extracts the body's chips into fresh
408
+ rows and the old ones become history rather than being deleted. The default read
409
+ already hides superseded rows, so `rel list` reflects the CURRENT content — you do
410
+ not need to filter it yourself.
411
+
412
+ **Caveat on `declared`:** chip tracking was added after the fact, so links created
413
+ before it report `declared` even when they DID come from a body chip. Read
414
+ `declared` as "not tracked to a chip", not as proof the link was never in the
415
+ prose — the note body is the authority. This only ever makes a link too durable
416
+ (it stays live), never too fragile, so `declared` is always safe to trust as
417
+ "still asserted"; just don't infer from it that the prose never said so.
418
+
353
419
  **See how a found note connects (the editor header, for agents).** When you
354
420
  land on a note via `search semantic` / `notes list`, the two affordances the
355
421
  editor shows at the top of the panel are both one call away — use them to
@@ -393,18 +459,6 @@ cnotes types update <name> [--display-name <name>] [--description <text>] [--col
393
459
  cnotes notes retype <displayId> --type <NewType>
394
460
  ```
395
461
 
396
- ### Memory (`cnotes memory` / `cnotes mem`)
397
- ```bash
398
- # Search facts and entities by relevance
399
- cnotes memory query "<query>" [--limit <n>] [--source <conversation|notes>]
400
-
401
- # List all workspace facts (extracted from notes and conversations)
402
- cnotes memory facts [--source <conversation|notes>]
403
-
404
- # List all entities (people, topics, orgs) identified by Zep
405
- cnotes memory entities [--source <conversation|notes>]
406
- ```
407
-
408
462
  ### Files (`cnotes files` / `cnotes f`)
409
463
  ```bash
410
464
  # Upload an image to the workspace (max 5MB, supports .jpg .jpeg .png .gif .webp)
@@ -588,59 +642,71 @@ If `status` is `"none"` or `"stale"`, the `notes` array is still populated and a
588
642
 
589
643
  ### Canvas Elements — When to Use What
590
644
 
645
+ The vocabulary is five elements:
646
+
647
+ | Element | What it is | CLI |
648
+ |---|---|---|
649
+ | **note** | A reusable knowledge object: typed, versioned, searchable, with a display ID other notes can mention | `add-node`, `place` `type:"note"` |
650
+ | **text** | Free-form rich text placed directly on the canvas — markdown (headings, emphasis, images), optional background tint (none by default). Wire type is `richtext`. | `add-text`, `place` `type:"richtext"` |
651
+ | **list** | A container whose content is its MEMBER NOTES; the description is a one-line frame | `add-list` |
652
+ | **section** | A named region frame (Miro-style). Mints `SECTION-<n>`, mention-addressable | `add-section` |
653
+ | **canvas** | A portal to another canvas | `add-link`, `place` `type:"canvas"` |
654
+
655
+ **There is no separate heading element.** A heading is a **text** element whose content is a markdown heading and which has no background tint: `add-text <canvasId> --content '# Q1 Goals'`. (An older `text` node kind — plain string + font size — is retired: existing ones still render and can be moved or deleted, but nothing creates them any more.)
656
+
591
657
  A canvas holds two tiers of material, and the difference is load-bearing:
592
658
 
593
- - **Notes** (note nodes) are the knowledge tier: typed, versioned, searchable, citable by display ID — the only tier whose content other notes can mention, search can find, and history records. Anything a future reader must find, cite, verify, or build on lives here.
594
- - **Everything else** — text labels, richtext stickies, list containers, edges, portals — is the presentation tier: orientation, grouping, emphasis, navigation. These have no display ID (nothing can cite them), no version history (they mutate silently), and no search presence. They shape how the knowledge lands; they never hold the knowledge.
659
+ - **Notes** are the knowledge tier: typed, versioned, searchable, citable by display ID — the only tier whose content other notes can mention, search can find, and history records. Anything a future reader must find, cite, verify, or build on lives here.
660
+ - **Everything else** — text elements, list containers, section frames, edges, portals — is the presentation tier: orientation, grouping, emphasis, navigation. Text elements, lists, edges and portals have no display ID (nothing can cite them), no version history (they mutate silently), and no search presence. (Sections are the one exception: a frame mints a `SECTION-<n>` you can mention — it addresses a *place*, not knowledge.) They shape how the knowledge lands; they never hold the knowledge.
595
661
 
596
- **The default is a typed note.** Before reaching for any other element, apply one test: *will any future reader — a human, or the next agent session pointed at this canvas for context — need to find, cite, or trust this?* If yes, it MUST be a note. "It renders nicely on the canvas" is never a reason to put knowledge in richtext: that trades permanent legibility (versions, search, citable IDs) for screen presence now. The decision-table rows below are the sanctioned exceptions — when a row matches your content exactly, the row wins.
662
+ **The default is a typed note.** Before reaching for any other element, apply one test: *will any future reader — a human, or the next agent session pointed at this canvas for context — need to find, cite, or trust this?* If yes, it MUST be a note. "It renders nicely on the canvas" is never a reason to put knowledge in a text element: that trades permanent legibility (versions, search, citable IDs) for screen presence now. The decision-table rows below are the sanctioned exceptions — when a row matches your content exactly, the row wins.
597
663
 
598
664
  #### Decision table
599
665
 
600
666
  | You have | Use | NOT |
601
667
  |---|---|---|
602
- | A claim, fact, decision, risk, question, verdict, score, spec, or example worth keeping | A typed **note** (right supertag, `# h1` title) | richtext |
603
- | N distinct things (questions, risks, options, findings) | **N notes** grouped in a **list**; `--description` = one-line frame | bullets in one richtext sticky or list `--description` |
604
- | A named REGION of the canvas ("Shaping", "Open Questions") | a **section** frame (`add-section`) — draggable region, mention-addressable as `[[SECTION-<n>]]` from note bodies | a text label floating over an implied area |
605
- | A one-line inline label where a frame is too heavy (a column header inside a frame, a lane key) | **text** node, `--size heading`, title case | richtext |
606
- | Prose telling the reader how to traverse THIS canvas | **richtext** orientation banner (one per canvas or band; up to ~5 sentences) | a Guide note nobody needs off-canvas |
607
- | Emphasis — restating a key claim for screen presence | **richtext** sticky that QUOTES a note (the note stays the home) | richtext as the only copy |
608
- | An image tile (logo, mockup, screenshot) | **richtext** image beside the owning note; embed the same image in the note body via `cnotes files upload <path> --markdown` so the note stays self-contained | the image as the knowledge itself |
609
- | A grid of images for review (mood board) | N richtext image tiles in a `grid` place spec + ONE note holding the decision criteria / rationale for the set | a caption note per tile, or rationale in the tiles |
668
+ | A claim, fact, decision, risk, question, verdict, score, spec, or example worth keeping | A typed **note** (right supertag, `# h1` title) | a text element |
669
+ | N distinct things (questions, risks, options, findings) | **N notes** grouped in a **list**; `--description` = one-line frame | bullets in one text element or in a list `--description` |
670
+ | A named REGION of the canvas ("Shaping", "Open Questions") | a **section** frame (`add-section`) — draggable region, mention-addressable as `[[SECTION-<n>]]` from note bodies | a floating text heading over an implied area |
671
+ | A heading / one-line label where a frame is too heavy (a column header inside a frame, a lane key) | a **text** element holding a markdown heading — `add-text --content '# Q1 Goals'`, no `--color` (leave it untinted), title case | a tinted card, or a section frame for something that isn't a region |
672
+ | Prose telling the reader how to traverse THIS canvas | a **text** orientation banner (one per canvas or band; up to ~5 sentences) | a Guide note nobody needs off-canvas |
673
+ | Emphasis — restating a key claim for screen presence | a **text** element that QUOTES a note (the note stays the home) | the text element as the only copy |
674
+ | An image tile (logo, mockup, screenshot) | a **text** element holding the image, beside the owning note; embed the same image in the note body via `cnotes files upload <path> --markdown` so the note stays self-contained | the image as the knowledge itself |
675
+ | A grid of images for review (mood board) | N text-element image tiles in a `grid` place spec + ONE note holding the decision criteria / rationale for the set | a caption note per tile, or rationale in the tiles |
610
676
  | "A connects to B" between two notes | a labeled **edge** (short verb phrase), notes only | prose explaining the connection |
611
- | A pointer to another canvas | a **portal** — `add-link` on the CLI, `{"type": "canvas", "linkedCanvasId": ...}` in place specs, returned as `canvasLinkNodes` by `canvas get` | a richtext "see other canvas" |
612
- | Backstage narration (scripts, speaker notes) | NOTES on a PRIVATE sibling canvas (cue-card richtext is fine there); portal FROM the private canvas TO the shared one, never the reverse | narration on the shared canvas |
677
+ | A pointer to another canvas | a **portal** — `add-link` on the CLI, `{"type": "canvas", "linkedCanvasId": ...}` in place specs, returned as `canvasLinkNodes` by `canvas get` | a text element saying "see other canvas" |
678
+ | Backstage narration (scripts, speaker notes) | NOTES on a PRIVATE sibling canvas (cue-card text elements are fine there); portal FROM the private canvas TO the shared one, never the reverse | narration on the shared canvas |
613
679
 
614
680
  #### Hard rules (each one has burned a real canvas)
615
681
 
616
- 1. **A canvas element must never be the sole home of a claim.** Verdict tallies, dates, scan provenance, and even a moat-defining sentence have each been authored as richtext stickies — then cited downstream while being unversioned, unsearchable, and uncitable. If a sticky says something true and useful, a note says it first; the sticky may quote the note. (Canvas-specific wayfinding — how to read THIS canvas — is the one exemption; it has no off-canvas value.)
617
- 2. **No relationship-mention syntax inside richtext or list descriptions.** `[NOTE-12](relationship:references)` and `[[NOTE-12]]` corrupt canvas richtext — the card renders blank. Reference notes in canvas prose as plain display IDs ("see RISK-64"). Mentions belong in NOTE bodies, where they create real graph edges.
618
- 3. **Richtext is capped at sticky size**: at most 3 sentences, one heading at most, never a list of distinct knowledge items (questions, risks, findings — those are N notes; a 3-line color legend or lane key is fine). The one exception is the orientation banner (table row 4, up to ~5 sentences). The cap is physical as much as doctrinal: richtext renders its full content inline, so long blocks explode the canvas — one canvas hit 43,000px tall from eight richtext reference blocks and had to be rebuilt as notes.
619
- 4. **Detail's HOME is the note body; richtext detail cards are projections.** Write the full spec/content INTO each note's body first. A parallel richtext card under a note is allowed only as a projection — it condenses or quotes a note that already contains everything, and says so ("from STAGE-3"). A detail card whose content exists nowhere else is the anti-pattern: one teaching canvas carried its entire method in richtext cards over one-line stub notes, and an agent reading it back got seven stubs and no method.
620
- 5. **Edges can connect any node type** (note, text, list, richtext, canvas-link), as long as both endpoints are on the same canvas; labels are short verb phrases ("fixes", "grounds", "depends on"). But `canvas read` does not return edges, so any relationship **between notes** that a reading agent must follow has to ALSO exist as a mention inside the note bodies — structure that lives only in a note-to-note edge is invisible on read-back. Edges to text/richtext/canvas-link nodes are diagram decoration (pointing a label at a cluster, wiring a portal) and carry no note-to-note relationship to mirror.
621
- 6. **Color is semantic or absent.** Default neutral (omit `--color`). Use a saturated hex only to encode a role applied consistently on this canvas (e.g. risks red, evidence blue), 2–4 roles max. If you cannot name the role, omit the color. Never pastels — they composite to mud on the canvas background.
682
+ 1. **A canvas element must never be the sole home of a claim.** Verdict tallies, dates, scan provenance, and even a moat-defining sentence have each been authored as text elements — then cited downstream while being unversioned, unsearchable, and uncitable. If a text element says something true and useful, a note says it first; the text element may quote the note. (Canvas-specific wayfinding — how to read THIS canvas — is the one exemption; it has no off-canvas value.)
683
+ 2. **No relationship-mention syntax inside text elements or list descriptions.** `[NOTE-12](relationship:references)` and `[[NOTE-12]]` corrupt a canvas text element — the card renders blank. Reference notes in canvas prose as plain display IDs ("see RISK-64"). Mentions belong in NOTE bodies, where they create real graph edges.
684
+ 3. **A text element is capped at card size**: at most 3 sentences, one heading at most, never a list of distinct knowledge items (questions, risks, findings — those are N notes; a 3-line color legend or lane key is fine). The one exception is the orientation banner (table row 5, up to ~5 sentences). A heading-only text element (`--content '# Q1 Goals'`) is of course fine — that is the smallest legitimate use. The cap is physical as much as doctrinal: a text element renders its full content inline, so long blocks explode the canvas — one canvas hit 43,000px tall from eight text reference blocks and had to be rebuilt as notes.
685
+ 4. **Detail's HOME is the note body; text detail cards are projections.** Write the full spec/content INTO each note's body first. A parallel text card under a note is allowed only as a projection — it condenses or quotes a note that already contains everything, and says so ("from STAGE-3"). A detail card whose content exists nowhere else is the anti-pattern: one teaching canvas carried its entire method in text cards over one-line stub notes, and an agent reading it back got seven stubs and no method.
686
+ 5. **Edges can connect any node type** (note, text, list, canvas-link — plus legacy heading nodes), as long as both endpoints are on the same canvas; labels are short verb phrases ("fixes", "grounds", "depends on"). `canvas read` returns edges, so they are no longer invisible on read-back — but an edge is still CANVAS-LOCAL: it is not part of the note graph, never appears in `rel list`, is not searchable, and means nothing once you leave this canvas. So any relationship **between notes** that must survive off the canvas has to ALSO exist as a mention inside the note bodies. The mention is the copy that travels; the edge is the copy you can see. Edges to text/list/canvas-link nodes are diagram decoration (pointing a label at a cluster, wiring a portal) and carry no note-to-note relationship to mirror.
687
+ 6. **Color is semantic or absent.** Default neutral — a text element has NO background unless you pass `--color`, and that is usually the right look (an untinted heading is just type on the canvas). Use a saturated hex only to encode a role applied consistently on this canvas (e.g. risks red, evidence blue), 2–4 roles max. If you cannot name the role, omit the color. Never pastels — they composite to mud on the canvas background.
622
688
 
623
689
  #### Two completion tests
624
690
 
625
691
  Run both before ending your operation:
626
692
 
627
- **The read-back test (for the next agent):** run `cnotes canvas read` and check that every claim is present as a NOTE section with a display ID, and the reasoning can be followed through note bodies and their mentions. Content that appears only as anonymous richtext prose fails the test even when visible — nothing can cite, search, or version it. List members read back as ID+title links without bodies; that is fine — the IDs are citable and batch-fetchable via `cnotes notes get`. As a heuristic for working canvases, at least ~4 notes per richtext element (zero richtext is fine); fewer means richtext is carrying content. Explainer canvases using the projection pattern (rule 4) are exempt from the ratio, not from the test.
693
+ **The read-back test (for the next agent):** run `cnotes canvas read` and check that every claim is present as a NOTE section with a display ID, and the reasoning can be followed through note bodies and their mentions. Content that appears only as anonymous text-element prose fails the test even when visible — nothing can cite, search, or version it. List members read back as ID+title links without bodies; that is fine — the IDs are citable and batch-fetchable via `cnotes notes get`. As a heuristic for working canvases, at least ~4 notes per text element (zero text elements is fine); fewer means text elements are carrying content. Explainer canvases using the projection pattern (rule 4) are exempt from the ratio, not from the test.
628
694
 
629
695
  **The glance test (for the human):** zoomed to fit, a person should grasp what this canvas argues and where to start reading in about 10 seconds. A canvas that passes read-back but renders as an undifferentiated card grid is also not done — that is what section labels, orientation banners, projections, and layout are for.
630
696
 
631
697
  #### Worked example
632
698
 
633
- Right shape (a real competitor scan): 31 typed notes (COMPETITOR profile, FACT evidence row, INSIGHT verdicts per dimension, RISK/IDEA/STRATEGY response bands, QUESTION follow-ups in a list), three short richtext banners framing the bands ("Threats, Steals, Response — what hurts us, what's worth taking, the plan"), and labeled edges mirrored as mentions (`FACT-44 --grounds--> INSIGHT-361`).
699
+ Right shape (a real competitor scan): 31 typed notes (COMPETITOR profile, FACT evidence row, INSIGHT verdicts per dimension, RISK/IDEA/STRATEGY response bands, QUESTION follow-ups in a list), three short text banners framing the bands ("Threats, Steals, Response — what hurts us, what's worth taking, the plan"), and labeled edges mirrored as mentions (`FACT-44 --grounds--> INSIGHT-361`).
634
700
 
635
- Wrong shape (same material): one 5,000-char richtext "deep scan writeup", a richtext scoreboard holding the verdict tally, and a few notes for leftovers. Search finds nothing, versions record nothing, nothing can cite or link any of it — and the headline verdict goes stale the first time someone edits the sticky.
701
+ Wrong shape (same material): one 5,000-char text element holding a "deep scan writeup", a text scoreboard holding the verdict tally, and a few notes for leftovers. Search finds nothing, versions record nothing, nothing can cite or link any of it — and the headline verdict goes stale the first time someone edits the card.
636
702
 
637
703
  #### Current platform limitations (re-check before relying on these)
638
704
 
639
705
  The durable rules above stand on versioning, search, and citability. Four rules also lean on today's platform behavior — if a future release changes these, the corresponding rule can relax:
640
706
 
641
- - Mention syntax corrupts canvas richtext / list descriptions (motivates rule 2; an @-mention affordance is planned).
642
- - `canvas read` omits edges and portals entirely, and returns list containers as their one-line frame only (motivates rule 5's mirror-as-mentions requirement).
643
- - Agents have no update verb for text/richtext/list elements — to change one, remove and re-add it (one more reason content that evolves belongs in notes, which have versions).
707
+ - Mention syntax corrupts canvas text elements / list descriptions (motivates rule 2; an @-mention affordance is planned).
708
+ - `canvas read` returns sections, edges and portals, but list containers still read back as their one-line frame only. An edge read back is still canvas-local and ungraphed (motivates rule 5's mirror-as-mentions requirement).
709
+ - Agents have no update verb for **text or list** elements — to change one, remove and re-add it (one more reason content that evolves belongs in notes, which have versions). Section frames are the exception: `canvas update-section` edits a frame in place.
644
710
 
645
711
  ### Wrapping batch work in an operation (ENFORCED for `notes create`)
646
712
 
@@ -652,7 +718,7 @@ When an AI agent (including you, Claude) is doing a **batch of work** — creati
652
718
 
653
719
  | Action | Wrap? |
654
720
  |---|---|
655
- | Reading: `digest`, `get`, `list`, `activity`, `timeline`, `search`, `memory query`, `rel list` | **No** — read-only |
721
+ | Reading: `digest`, `get`, `list`, `activity`, `timeline`, `search`, `rel list` | **No** — read-only |
656
722
  | **Any `cnotes notes create`** (even a single note) | **Yes — enforced.** Creates run through the batch endpoint, which now rejects writes with no active run |
657
723
  | A single `notes update` / tag / pin / archive change | **No** — single metadata edits don't go through the guarded create path |
658
724
  | Two or more writes that belong to one intent (multi-note batch, canvas build-out, multi-step refactor) | **Yes** |
@@ -668,7 +734,7 @@ cnotes operations begin --prompt "Reorganize roadmap into quarters" --json
668
734
 
669
735
  # 2. Do the work. Every cnotes write here attaches to the run automatically.
670
736
  cnotes notes create --notes '[ ... ]'
671
- cnotes canvas add-text <canvasId> --text "Q1" --size heading --x 0 --y 0
737
+ cnotes canvas add-text <canvasId> --content '# Q1' --x 0 --y 0
672
738
  cnotes canvas place <canvasId> --spec /tmp/q1.json
673
739
  cnotes canvas add-edge <canvasId> --source <a> --target <b> --label "depends on"
674
740
 
@@ -743,14 +809,16 @@ The schema is strict: an unknown or typo'd key anywhere in the doc (e.g. `gapp`,
743
809
 
744
810
  ```json
745
811
  { "kind": "item", "type": "note", "noteId": "NOTE-3", "key": "a" }
746
- { "kind": "item", "type": "text", "content": "Hello", "fontSize": 32, "colorVariant": "muted" }
747
- { "kind": "item", "type": "richtext", "content": "# Markdown OK", "size": "medium" }
812
+ { "kind": "item", "type": "richtext", "content": "# Q1 Goals" }
813
+ { "kind": "item", "type": "richtext", "content": "Read this band left to right.", "size": "medium", "colorHex": "#c2410c" }
748
814
  { "kind": "item", "type": "list", "description": "Risks tracked weekly — owner: @ana", "noteIds": ["A","B","C"], "viewMode": "list" }
749
815
  { "kind": "item", "type": "canvas", "linkedCanvasId": "abc..." }
750
816
  { "kind": "item", "type": "section", "name": "Open Questions", "width": 800, "height": 500 }
751
817
  ```
752
818
 
753
- Leaf enums (exact values): text `colorVariant` = `normal` | `muted` | `highlighted`, `fontSize` 8–72; richtext `size` = `small` | `medium` | `large`; list `viewMode` = `list` | `grid`, list `size` = `sm` | `md` | `lg` | `xl`.
819
+ `richtext` IS the **text** element (the CLI's `add-text`) — the only free-form text leaf. A heading is a `richtext` leaf whose content is `"# Title"` with no `colorHex`. There is no `"type": "text"` leaf: the old heading node kind is retired and the layout engine rejects it.
820
+
821
+ Leaf enums (exact values): richtext `size` = `small` | `medium` | `large`, `colorHex` = a saturated hex (omit for no background); list `viewMode` = `list` | `grid`, list `size` = `sm` | `md` | `lg` | `xl`.
754
822
 
755
823
  #### Section container (frame WITH content — preferred)
756
824
 
@@ -774,12 +842,15 @@ surface it and mention it as `[[SECTION-<n>]]` from note bodies. For the
774
842
  simple case, `add-section --notes id1,id2` is the same thing as a one-liner.
775
843
  To frame content that ALREADY sits on the canvas, add an empty `add-section`
776
844
  with bounds enclosing it (frames aren't collision obstacles, overlap is fine).
845
+ To grow or rename a frame that already exists, use `canvas update-section` —
846
+ never delete + re-add, which mints a new SECTION-<n> and breaks every
847
+ `[[SECTION-<n>]]` mention pointing at the old one.
777
848
 
778
849
  The optional `key` lets edges and other items reference this leaf later (`@<key>`).
779
850
 
780
- `richtext.content` accepts markdown directly — the server converts to TipTap before measuring. Plain markdown only: NO `[NOTE-X](relationship:...)` / `[[...]]` mention syntax (it corrupts the card — reference notes as plain display IDs), and keep it sticky-sized per "Canvas Elements — When to Use What".
851
+ `richtext.content` (the **text** element) accepts markdown directly — the server converts to TipTap before measuring. Plain markdown only: NO `[NOTE-X](relationship:...)` / `[[...]]` mention syntax (it corrupts the card — reference notes as plain display IDs), and keep it card-sized per "Canvas Elements — When to Use What".
781
852
 
782
- `list.description` is a one-line **frame**, never the content itself — the content is `noteIds` (the member notes). For N distinct things, create N notes (right type per item, e.g. `Question` for open questions) and list their ids. A `list` with a multi-item description and an empty `noteIds` is the wrong shape — use a sticky-sized `richtext` (at most ~3 sentences) only for a short framing block with no members; anything longer, or any set of distinct items, is N notes.
853
+ `list.description` is a one-line **frame**, never the content itself — the content is `noteIds` (the member notes). For N distinct things, create N notes (right type per item, e.g. `Question` for open questions) and list their ids. A `list` with a multi-item description and an empty `noteIds` is the wrong shape — use a card-sized **text** element (`richtext`, at most ~3 sentences) only for a short framing block with no members; anything longer, or any set of distinct items, is N notes.
783
854
 
784
855
  #### Edges
785
856
 
@@ -896,8 +967,8 @@ Edges live at the top level alongside `root`. Each endpoint is resolved in this
896
967
 
897
968
  - **Anchors take zero space in their parent** — when nested inside a `stack` or `grid`, an anchor reserves no slot, so siblings collapse together. Anchors are best used at the root or as their own top-level branch.
898
969
  - **Anchor `to:` must be a node already on the canvas** — local `@key` refs only work for edges, not for anchor targets in this version.
899
- - **Never use ALL CAPS** for text node content. Use title case (e.g., "Key Tensions" not "KEY TENSIONS").
900
- - **Multi-line richtext content** is best authored as markdown — the server converts it server-side. Keep it sticky-sized (at most ~3 sentences; see Canvas Elements hard rule 3).
970
+ - **Never use ALL CAPS** in a text element — headings included. Use title case (e.g., "Key Tensions" not "KEY TENSIONS").
971
+ - **Text-element content** is authored as markdown — the server converts it server-side. A heading is `"# Key Tensions"` with no `colorHex`. Keep it card-sized (at most ~3 sentences; see Canvas Elements hard rule 3).
901
972
 
902
973
  #### When the layout engine isn't enough
903
974
 
@@ -921,7 +992,7 @@ When creating multiple related notes, interlink at two levels:
921
992
  - After adding nodes, connect them with `cnotes canvas add-edge`, using short verb labels ("triggers", "depends on").
922
993
  - Edges can join any node type, as long as both endpoints are on the same canvas.
923
994
 
924
- Both levels are needed — edges are visual-only, mentions are content-level — and `cnotes canvas read` returns note bodies but NOT edges, so any relationship that matters must also exist as a mention in the note bodies (see Canvas Elements hard rules 2 and 5).
995
+ Both levels are needed — edges are canvas-local, mentions are content-level. `cnotes canvas read` does return edges, but only the mention creates a real graph edge that `rel list` and search can see, so any relationship that matters must also exist as a mention in the note bodies (see Canvas Elements hard rules 2 and 5).
925
996
 
926
997
  #### Placeholder Syntax for `cnotes notes create`
927
998
 
@@ -932,14 +1003,14 @@ This builds on [@B: The Problem](relationship:references) and enables [@C: The W
932
1003
 
933
1004
  The relationship type is free-form, but **reuse an existing type before inventing one** — see [Content via Markdown](#content-via-markdown) above for the reuse rule, why needless synonyms are harmful, and the built-in types to reach for first.
934
1005
 
935
- ### Memory
1006
+ ### Gathering Context with Search
936
1007
 
937
- Before creating or updating notes on a topic, **query memory first** to understand what is already known:
1008
+ Before creating or updating notes on a topic, **search existing notes first**:
938
1009
 
939
- 1. **Start broad:** `cnotes memory query "<topic>"` — get the top facts and entities.
940
- 2. **Follow entities:** Run follow-up queries on related people, teams, or initiatives.
941
- 3. **Check temporal validity:** Facts have `validAt`/`invalidAt` timestamps — prefer current facts.
942
- 4. **Synthesize:** Combine facts from 2-3 queries before acting.
1010
+ 1. **Notes:** `cnotes search semantic "<topic>"` — find conceptually related notes.
1011
+ 2. **Canvases:** `cnotes canvas search "<topic>"` — separate corpus from notes.
1012
+ 3. **Follow up:** Open hits with `cnotes notes get` / `cnotes canvas get`, then chase `rel list` and in-note mentions.
1013
+ 4. **Synthesize:** Combine what you found from 2-3 searches before acting.
943
1014
 
944
1015
  ### Error Handling
945
1016
  - Branch on the **exit code** first (see the table in [Reading output](#reading-output-start-here)) — it classes the failure without parsing prose — then read the `hint` in the `{ error, code, exitCode, hint }` JSON error on stderr; it names the command that fixes it.