{ "argumentSchema": { "$defs": { "TopicAuthor": { "properties": { "avatar": { "type": "string" }, "kind": { "description": "Open for the same reason `TopicLinkKind` is: this is provided data, so a\nclosed set here could never gain `\"service\"` or whatever acts next.\nWell-known values are `\"person\"` and `\"agent\"`, the latter marking an\nagent acting with its human user's key.", "type": "string" }, "name": { "type": "string" } }, "required": [ "kind", "name" ], "type": "object" }, "TopicComment": { "properties": { "author": { "$ref": "#/$defs/TopicAuthor", "description": "Snapshot taken at write time (profile enrichment comes later; never gate\nauthorship on a profile wish — CT-1879). Comments carry no minted id:\narray elements have stable entity identity; future editing addresses\nelements by reference (`equals()`), not by a synthetic key.\n\nOptional because durable Topics may contain a comment without structured\nauthorship. Current `addComment` calls require `agentName` and always write\nthis snapshot." }, "body": { "default": "", "type": "string" }, "editedAt": { "description": "When the body was last revised, absent on a comment never edited. The\noriginal `sentAt` and `author` are left alone: an edit changes what was\nsaid, not who said it or when the thread reached this point.", "type": "number" }, "removedAt": { "description": "Set when the comment was retracted, and the retraction is the whole of\nit — the record stays, carrying what it always said. A reader hides it;\n`commentCount` stops counting it; `lastActivityOf` keeps reading its\n`sentAt`, which is what stops a retraction moving a topic backwards in\nthe board's ordering.", "type": "number" }, "removedBy": { "$ref": "#/$defs/TopicAuthor" }, "sentAt": { "default": 0, "type": "number" } }, "required": [ "body", "sentAt" ], "type": "object" }, "TopicCrossrefRow": { "properties": { "mentionedBy": { "items": { "type": "unknown" }, "type": "array" }, "topic": { "description": "The topic this row is about. `unknown` because it is written as a\nreference and only ever compared — `equals` takes the raw link. Anything\nwider retrieves the piece instead of pointing at it: declared `object`,\nthis field reads back as the whole expanded topic, `$UI` tree included.", "type": "unknown" } }, "required": [ "topic", "mentionedBy" ], "type": "object" }, "TopicLink": { "properties": { "addedAt": { "type": "number" }, "addedBy": { "$ref": "#/$defs/TopicAuthor" }, "kind": { "default": "web", "type": "string" }, "label": { "default": "", "type": "string" }, "removedAt": { "description": "As on a comment, and with one consequence of its own: a retracted link\nstops resolving into `mentions`, so the reference it contributed goes\nwith it rather than outliving the link that made it.", "type": "number" }, "removedBy": { "$ref": "#/$defs/TopicAuthor" }, "url": { "default": "", "type": "string" } }, "required": [ "kind", "url", "label" ], "type": "object" }, "TopicMentionRef": { "properties": { "destination": { "type": "unknown" }, "modifiedTitle": { "type": "boolean" } }, "required": [ "destination", "modifiedTitle" ], "type": "object" }, "TopicMentionRefMap": { "additionalProperties": { "$ref": "#/$defs/TopicMentionRef" }, "properties": {}, "type": "object" }, "TopicMentionable": { "properties": { "$NAME": { "default": "", "type": [ "string", "undefined" ] }, "title": { "default": "", "type": "string" } }, "required": [ "title", "$NAME" ], "type": "object" } }, "properties": { "boardCrossrefs": { "asCell": [ "readonly" ], "default": [], "description": "The board's mention pivot, one row per topic. A topic reads its own row\nout of it and nothing else; see `backlinksOf`. Absent, a topic simply shows\nno inbound references.\n\nReadable, not writable: the pivot is the board's derivation, and a topic\nhas no business writing into it. Declaring the narrower cell says so where\na reader can see it, rather than leaving it to convention.", "items": { "$ref": "#/$defs/TopicCrossrefRow" }, "type": "array" }, "body": { "asCell": [ "cell" ], "default": "", "description": "The topic's living document: durable conclusions get folded up into the\nbody; the comment thread below holds the deliberation.", "type": "string" }, "bodyUpdatedAt": { "asCell": [ "cell" ], "default": 0, "type": "number" }, "bodyUpdatedBy": { "$ref": "#/$defs/TopicAuthor", "asCell": [ "cell" ], "default": { "kind": "person", "name": "" } }, "comments": { "asCell": [ "cell" ], "default": [], "items": { "$ref": "#/$defs/TopicComment" }, "type": "array" }, "createdAt": { "default": 0, "type": "number" }, "createdBy": { "$ref": "#/$defs/TopicAuthor" }, "links": { "asCell": [ "cell" ], "default": [], "items": { "$ref": "#/$defs/TopicLink" }, "type": "array" }, "mentionable": { "asCell": [ "cell" ], "default": [], "description": "The board's mention universe — what the body editor autocompletes\nover. Wired at creation to the board's mention index (one derived\ndocument of two-string rows; `TopicMentionableRow` in `main.tsx`), and\nrewired onto a piece from before the index as a one-time link-bind.\nA plain list of the pieces themselves also satisfies the demand — a\nboard not yet rewired, or a composer without a board. Absent, the\neditor simply offers no completions.", "items": { "$ref": "#/$defs/TopicMentionable" }, "type": "array" }, "mentioned": { "asCell": [ "cell" ], "default": [], "description": "Pieces this topic references outside its prose — what `mention` records.\n\nIts own list rather than an entry in `references`, because that map belongs\nto the body editor: the editor mints its keys, rewrites its labels, and\ncollects entries whose token has left the document. A verb writing there\nwould be writing into somebody else's bookkeeping. Here there is nothing to\nkey by, because there is nothing to point back at from the prose — a\nreference outside a sentence is just a link in a list.", "items": { "type": "unknown" }, "type": "array" }, "references": { "$ref": "#/$defs/TopicMentionRefMap", "asCell": [ "cell" ], "default": {}, "description": "Where this topic's `[Label][key]` mentions point, keyed by the token that\nappears in the body. The editor owns the contents; this pattern owns the\ncell, which is what makes a mention durable and — because each entry holds\nthe destination as a REFERENCE — what makes the reference graph a question\nabout cell identity rather than about text.\n\nThe default has to match the one `TopicOutput` publishes. A map published\nunder a different default than its input carries cannot be materialized." }, "title": { "asCell": [ "cell" ], "default": "", "type": "string" }, "titleUpdatedAt": { "asCell": [ "cell" ], "default": 0, "type": "number" }, "titleUpdatedBy": { "$ref": "#/$defs/TopicAuthor", "asCell": [ "cell" ], "default": { "kind": "person", "name": "" }, "description": "Attribution of the last rename, stamped by `setTitle` — the same pair\nthe body keeps, because a title is the other editable scalar." } }, "type": "object" }, "pattern": "topics/topic.tsx", "resultSchema": { "$defs": { "AddCommentEvent": { "properties": { "agentName": { "description": "Explicit content-level signature for an agent using its human user's\nidentity key. Fabric authenticates the write with that key; this says\nwhich agent acted under it.\n\nRequired so every authored-content call carries structured attribution.\nAuthenticated execution provenance can supersede the field by relaxing it\nto optional without breaking existing callers.", "type": "string" }, "body": { "description": "The comment text, appended verbatim after trimming. Must be non-empty:\nan empty body rejects rather than recording a blank thread entry.", "type": "string" } }, "required": [ "body", "agentName" ], "type": "object" }, "AddLinkEvent": { "properties": { "agentName": { "description": "Explicit content-level signature for an agent using its human user's\nidentity key. Fabric authenticates the write with that key; this says\nwhich agent acted under it.\n\nRequired so every authored-content call carries structured attribution.\nAuthenticated execution provenance can supersede the field by relaxing it\nto optional without breaking existing callers.", "type": "string" }, "kind": { "description": "What the link points at — a rendering hint, not a behavior switch.\nOptional because the handler defaults it to `\"web\"`, and a caller should\nnot be made to supply a field the verb never needed.", "type": "string" }, "label": { "description": "Display label. Optional, and a blank one falls back to the URL.", "type": "string" }, "url": { "description": "The link target. Must be http(s): anything else rejects, because a\nuser-supplied scheme on a shared surface is script execution in every\nviewer's session.", "type": "string" } }, "required": [ "url", "agentName" ], "type": "object" }, "EditCommentEvent": { "properties": { "agentName": { "description": "Explicit content-level signature for an agent using its human user's\nidentity key. Fabric authenticates the write with that key; this says\nwhich agent acted under it.\n\nRequired so every authored-content call carries structured attribution.\nAuthenticated execution provenance can supersede the field by relaxing it\nto optional without breaking existing callers.", "type": "string" }, "body": { "description": "The complete replacement text, trimmed. Must be non-empty: emptying a\ncomment is a retraction, and `removeComment` is what says so.", "type": "string" }, "comment": { "description": "The comment to revise, named and checked as in `RemoveCommentEvent`,\nand additionally naming `body`, which this verb writes.", "properties": { "body": { "default": "", "type": "string" }, "editedAt": { "type": "number" }, "removedAt": { "type": "number" }, "sentAt": { "default": 0, "type": "number" } }, "required": [ "sentAt", "body" ], "type": "object" } }, "required": [ "comment", "body", "agentName" ], "type": "object" }, "MentionEvent": { "properties": { "topic": { "description": "The piece to reference — the piece itself, not an address. Identity here\nis the cell, so this is what a caller passes and what gets stored.\n\nDeclared through the one field every Topic has rather than `unknown`. The\ncell declaration marks this as a reference position, so the CLI resolves a\ncanonical `/of:...` value from an inline JSON event into the live piece\nlink. It also bounds the validation read: `topic.get()` is `undefined` for\na value that is not a reference and an object for any piece, and `mention`\nchecks it before storing anything.\n\n`title` is also the most this can safely name: this schema reaches every\ntopic in `mentionable`, so a property without a default would be demanded\nof topics written before it existed and refuse their update. `title`\ncarries one, and that default does double duty — it is also why the check\nadmits a piece that is not a topic at all, which reads back `{ title: \"\" }`\nrather than `undefined`. `deno task pattern-vintage` proves the update side\nby replaying a real board.\n\nNothing reads THROUGH it beyond that one field — the value is stored and\ncompared.", "properties": { "title": { "default": "", "type": "string" } }, "required": [ "title" ], "type": "object" } }, "required": [ "topic" ], "type": "object" }, "RemoveCommentEvent": { "properties": { "agentName": { "description": "Explicit content-level signature for an agent using its human user's\nidentity key. Fabric authenticates the write with that key; this says\nwhich agent acted under it.\n\nRequired so every authored-content call carries structured attribution.\nAuthenticated execution provenance can supersede the field by relaxing it\nto optional without breaking existing callers.", "type": "string" }, "comment": { "description": "The comment to retract — the stored element, not a copy of its fields.\nNames only what this verb reads and writes, for the reason\n`MentionEvent.topic` names only `title`: the declaration bounds the\nvalidation read, and a payload that is not a reference reads back\n`undefined` and is refused rather than silently matching nothing.\n`sentAt` carries a default, so it is what makes a real element read back\nas an object; the stamp fields are optional, which is what lets this\nschema reach comments written before they existed.", "properties": { "removedAt": { "type": "number" }, "removedBy": { "$ref": "#/$defs/TopicAuthor" }, "sentAt": { "default": 0, "type": "number" } }, "required": [ "sentAt" ], "type": "object" } }, "required": [ "comment", "agentName" ], "type": "object" }, "RemoveLinkEvent": { "properties": { "agentName": { "description": "Explicit content-level signature for an agent using its human user's\nidentity key. Fabric authenticates the write with that key; this says\nwhich agent acted under it.\n\nRequired so every authored-content call carries structured attribution.\nAuthenticated execution provenance can supersede the field by relaxing it\nto optional without breaking existing callers.", "type": "string" }, "link": { "description": "The stored link element. What a reader passes, from the row it renders.\nNamed as in `RemoveCommentEvent`, with `url` playing the defaulted-field\nrole `sentAt` plays there.", "properties": { "removedAt": { "type": "number" }, "removedBy": { "$ref": "#/$defs/TopicAuthor" }, "url": { "default": "", "type": "string" } }, "required": [ "url" ], "type": "object" }, "url": { "description": "The link's URL, for a caller that cannot pass a reference. Retracts the\nmost recently added link still present with this URL, so retracting twice\nretracts two rather than re-stamping one.\n\nSequentially. Two concurrent retractions naming the same URL both resolve\nthe same newest-present element and merge into one stamped record, so\nthey retract one link between them rather than two. Naming the link by\nreference is what makes a caller's target unambiguous under concurrency;\nthis spelling trades that for being reachable at all.", "type": "string" } }, "required": [ "agentName" ], "type": "object" }, "SetBodyEvent": { "properties": { "agentName": { "description": "Explicit content-level signature for an agent using its human user's\nidentity key. Fabric authenticates the write with that key; this says\nwhich agent acted under it.\n\nRequired so every authored-content call carries structured attribution.\nAuthenticated execution provenance can supersede the field by relaxing it\nto optional without breaking existing callers.", "type": "string" }, "body": { "description": "The complete replacement body, persisted verbatim — no trimming, so\nwhitespace-sensitive Markdown survives. An empty body is legal: clearing\na living document is an edit, not an error.", "type": "string" } }, "required": [ "body", "agentName" ], "type": "object" }, "SetTitleEvent": { "properties": { "agentName": { "description": "The agent making this mutation, stored as structured attribution beside\nthe write time. Required so every successful rename is attributed.", "type": "string" }, "title": { "description": "The new title, trimmed before it is stored. Must be non-empty.", "type": "string" } }, "required": [ "title", "agentName" ], "type": "object" }, "TopicAuthor": { "properties": { "avatar": { "type": "string" }, "kind": { "description": "Open for the same reason `TopicLinkKind` is: this is provided data, so a\nclosed set here could never gain `\"service\"` or whatever acts next.\nWell-known values are `\"person\"` and `\"agent\"`, the latter marking an\nagent acting with its human user's key.", "type": "string" }, "name": { "type": "string" } }, "required": [ "kind", "name" ], "type": "object" }, "TopicComment": { "properties": { "author": { "$ref": "#/$defs/TopicAuthor", "description": "Snapshot taken at write time (profile enrichment comes later; never gate\nauthorship on a profile wish — CT-1879). Comments carry no minted id:\narray elements have stable entity identity; future editing addresses\nelements by reference (`equals()`), not by a synthetic key.\n\nOptional because durable Topics may contain a comment without structured\nauthorship. Current `addComment` calls require `agentName` and always write\nthis snapshot." }, "body": { "default": "", "type": "string" }, "editedAt": { "description": "When the body was last revised, absent on a comment never edited. The\noriginal `sentAt` and `author` are left alone: an edit changes what was\nsaid, not who said it or when the thread reached this point.", "type": "number" }, "removedAt": { "description": "Set when the comment was retracted, and the retraction is the whole of\nit — the record stays, carrying what it always said. A reader hides it;\n`commentCount` stops counting it; `lastActivityOf` keeps reading its\n`sentAt`, which is what stops a retraction moving a topic backwards in\nthe board's ordering.", "type": "number" }, "removedBy": { "$ref": "#/$defs/TopicAuthor" }, "sentAt": { "default": 0, "type": "number" } }, "required": [ "body", "sentAt" ], "type": "object" }, "TopicLink": { "properties": { "addedAt": { "type": "number" }, "addedBy": { "$ref": "#/$defs/TopicAuthor" }, "kind": { "default": "web", "type": "string" }, "label": { "default": "", "type": "string" }, "removedAt": { "description": "As on a comment, and with one consequence of its own: a retracted link\nstops resolving into `mentions`, so the reference it contributed goes\nwith it rather than outliving the link that made it.", "type": "number" }, "removedBy": { "$ref": "#/$defs/TopicAuthor" }, "url": { "default": "", "type": "string" } }, "required": [ "kind", "url", "label" ], "type": "object" }, "TopicMentionRef": { "properties": { "destination": { "type": "unknown" }, "modifiedTitle": { "type": "boolean" } }, "required": [ "destination", "modifiedTitle" ], "type": "object" }, "TopicMentionRefMap": { "additionalProperties": { "$ref": "#/$defs/TopicMentionRef" }, "properties": {}, "type": "object" }, "TopicSummary": { "properties": { "commentCount": { "default": 0, "type": [ "number", "undefined" ] }, "createdAt": { "description": "When the topic was filed (epoch milliseconds), stamped at create.", "type": "number" }, "createdBy": { "anyOf": [ { "$ref": "#/$defs/TopicAuthor" }, { "type": "undefined" } ], "default": { "kind": "person", "name": "" } }, "lastActivityAt": { "default": 0, "type": [ "number", "undefined" ] }, "title": { "default": "", "description": "The topic's title. Defaulted rather than required, like every other\nfield a board card renders: the card list is a mapped sub-pattern, so\nthis type reaches a piece holding topics written before the field\nexisted as the argument schema its update is checked against, and a\nrequired property those topics lack refuses that update outright.\n`deno task pattern-vintage` is what catches it, by replaying a real\ndeployed board.", "type": "string" } }, "required": [ "title", "createdAt", "commentCount", "lastActivityAt" ], "type": "object" }, "UnmentionEvent": { "properties": { "topic": { "description": "Declared and checked exactly as `MentionEvent.topic` is, and for the same\nreason: a payload that is not a reference matches no stored entry, so\nwithout the check it would remove nothing and report success.", "properties": { "title": { "default": "", "type": "string" } }, "required": [ "title" ], "type": "object" } }, "required": [ "topic" ], "type": "object" } }, "description": "A #topic — one durable unit of shared attention: a title, a living body\ndocument, a flat chronological comment thread, typed links out to other\ncore objects (PRs, agent sessions, web pages), and the references it makes\nto sibling pieces.\n\nDurable conclusions get folded up into the body — revise it whole with\n`setBody`; the thread holds the deliberation as point-in-time `addComment`\nrecords. The thread only ever grows: `removeComment` and `removeLink` stamp\na record as retracted and leave it where it is, and `editComment` revises a\nbody in place, so what a reader shows is the array filtered rather than the\narray itself. Sign every authored-content mutation with `agentName`:\nFabric records the human principal behind the key; the name says which\nagent acted under it. Reference-only `mention` and `unmention` calls carry\nno content signature. The session-draft cells and `submit*` streams below\nbelong to the rendered page, not the headless contract — they read state\nonly this session holds.", "properties": { "$NAME": { "default": "", "description": "The topic's display name. Like the other derived display fields, a cold\nretained topic may not have produced this path yet; its persisted title\nremains authoritative until it does.", "type": [ "string", "undefined" ] }, "$UI": { "$ref": "https://commonfabric.org/schemas/vnode.json" }, "addComment": { "$ref": "#/$defs/AddCommentEvent", "asCell": [ "stream" ], "description": "Append to the thread — a point-in-time record of progress or\ndeliberation; durable conclusions belong in the body. Returns the\ncomment as recorded, resolved author and `sentAt` included." }, "addLink": { "$ref": "#/$defs/AddLinkEvent", "asCell": [ "stream" ], "description": "Attach a typed outbound link — a PR, an agent session, a web page.\nReturns the link as recorded, resolved `addedBy` and `addedAt`\nincluded. Appends merge and nothing dedupes: a repeated URL is two\nentries." }, "body": { "default": "", "description": "The living document, verbatim Markdown. `setBody` replaces it whole.", "type": "string" }, "bodyDraft": { "type": "string" }, "bodyUpdatedAt": { "type": [ "number", "undefined" ] }, "bodyUpdatedBy": { "anyOf": [ { "type": "undefined" }, { "$ref": "#/$defs/TopicAuthor" } ] }, "cancelEditBody": { "asCell": [ "stream", "opaque" ], "description": "UI wrapper: close the body editor; the session drafts are the discard.", "tier": "wrapper" }, "cancelEditTitle": { "asCell": [ "stream", "opaque" ], "description": "UI wrapper: close the rename editor; the session draft is the discard.", "tier": "wrapper" }, "commentCount": { "default": 0, "type": [ "number", "undefined" ] }, "commentDraft": { "description": "Session-local composer/edit state. These controls belong to a direct Topic\ninstance, not the shared TopicPiece projection used by the tracker's list.", "type": "string" }, "comments": { "description": "The thread, in arrival order, each record carrying its author snapshot\nand `sentAt`.\n\nMEMBERSHIP is append-only; a record is not. Nothing is ever removed from\nthis array — `removeComment` stamps `removedAt` and leaves the record in\nplace, and `editComment` revises a body and stamps `editedAt` — so a\nreader that wants the live thread filters on `removedAt` rather than\ntaking the array as it stands. `commentCount` is that filtered count.", "items": { "$ref": "#/$defs/TopicComment" }, "type": "array" }, "createdAt": { "description": "When the topic was filed (epoch milliseconds), stamped at create.", "type": "number" }, "createdBy": { "anyOf": [ { "$ref": "#/$defs/TopicAuthor" }, { "type": "undefined" } ], "default": { "kind": "person", "name": "" } }, "editComment": { "$ref": "#/$defs/EditCommentEvent", "asCell": [ "stream" ], "description": "Revise a comment's body in place, naming it by reference. Stamps\n`editedAt` and leaves `author` and `sentAt` as they were. Outside the\nprojection for the reason `removeComment` states." }, "editingBody": { "scope": "session", "type": "boolean" }, "editingTitle": { "scope": "session", "type": "boolean" }, "lastActivityAt": { "default": 0, "type": [ "number", "undefined" ] }, "linkKindDraft": { "type": "string" }, "linkLabelDraft": { "type": "string" }, "linkUrlDraft": { "type": "string" }, "links": { "description": "Typed outbound links, in arrival order, each carrying its author snapshot\nand `addedAt`. Append-only in membership on the same terms as `comments`:\n`removeLink` stamps rather than removes, and a stamped link additionally\nstops resolving into `mentions`.", "items": { "$ref": "#/$defs/TopicLink" }, "type": "array" }, "mention": { "$ref": "#/$defs/MentionEvent", "asCell": [ "stream" ], "description": "Reference another piece from this topic — the payload is the piece\nitself, stored as a reference. Takes no `agentName` and returns no value;\nFabric retains the authenticated principal behind the edge." }, "mentioned": { "default": [], "description": "Pieces referenced outside the prose, recorded by `mention`.", "items": { "type": "unknown" }, "type": "array" }, "mentions": { "default": [], "description": "Every piece this topic's prose and links point at, as references.\n\nThe board's pivot is declared over this and nothing else, so what one topic\ncosts a full-board scan is exactly this list of identities. Declared\n`unknown[]` because these are references, and comparing them never reads\nwhat is behind them.\n\nThe cell annotation that makes them comparable belongs to the READER, not\nhere: the pivot declares its own view of this list as\n`TopicMentionSource.mentions: ComparableCell[]`, and that is what\nmakes its graph settle rather than depend on load order. A published\n`unknown[]` is what each reader annotates for itself.\n\nThe default stands alone: a declared default and `| undefined` collide,\nand the default is what a topic deployed before this path existed\nmaterializes against.", "items": { "type": "unknown" }, "type": "array" }, "referencedBy": { "default": [], "description": "The topics that mention this one, read out of the board's pivot.\n\nDeclared through `TopicSummary` rather than `TopicPiece`, and that is\nload-bearing rather than stingy: a topic whose backlinks were topics would\nbe a type that contains itself, and resolving one from a list would walk\nthe graph. The summary carries no reference of its own, so it terminates.\nA reader that wants more follows the link, which resolves whole.", "items": { "$ref": "#/$defs/TopicSummary" }, "type": "array" }, "references": { "$ref": "#/$defs/TopicMentionRefMap", "default": {}, "description": "Where this topic's `[Label][key]` mentions point. Durable content, like\n`links`. The body editor works on a session copy of this map, which a save\npublishes here whole, beside the prose whose tokens name its entries." }, "referencesDraft": { "$ref": "#/$defs/TopicMentionRefMap", "description": "The body draft's mention map, staged so Cancel can discard it.\n\n`cf-code-editor` writes an entry the moment a mention is inserted and\ndrops one the moment its token leaves the document, neither of them\nwaiting for Save. Pointed at the durable map while `$value` holds a\nsession draft, those writes would outlive a discarded edit: a canceled\ninsertion would leave an edge no token names, and a canceled deletion\nwould strip the destination from a token the durable body still carries.\nThe editor's own ordering assumes its two bindings share a lifetime, so\nthis gives them one." }, "removeComment": { "$ref": "#/$defs/RemoveCommentEvent", "asCell": [ "stream" ], "description": "Retract a comment, naming it by reference. The record is stamped rather\nthan deleted, so the thread keeps its evidence while a reader stops\nshowing it and `commentCount` stops counting it.\n\nHere rather than on `TopicPiece`, for the reason `setTitle` is: boards\nstore that projection, so it is a demand on every topic they already\nhold, and a required verb added to it would refuse every one deployed\nbefore the verb existed. A verb has no default to be rescued by, so there\nis no compatible way to demand a new one. Addressing the topic directly\nreaches all three of these." }, "removeLink": { "$ref": "#/$defs/RemoveLinkEvent", "asCell": [ "stream" ], "description": "Retract a link, by reference or by `url`. Stamped like a comment, and a\nretracted link stops resolving into `mentions`. The url spelling exists\nbecause a link record carries no fid for a CLI caller to name. Outside\nthe projection for the reason `removeComment` states." }, "saveBody": { "asCell": [ "stream", "opaque" ], "description": "UI wrapper: publish the session body and mention-map drafts whole,\nattributed to the viewer's Profile — the contract verb is `setBody`.", "tier": "wrapper" }, "saveTitle": { "asCell": [ "stream", "opaque" ], "description": "UI wrapper: save the session title draft under the viewer's Profile —\nthe contract verb is `setTitle`, and both land through the same core, so\na browser rename stamps the same attribution and moves the same activity\nclock.", "tier": "wrapper" }, "setBody": { "$ref": "#/$defs/SetBodyEvent", "asCell": [ "stream" ], "description": "Replace the living document whole — read it, revise it, write it back\ncomplete; the body is one value with whole-value conflict semantics.\nRequires `agentName` and returns the persisted body with its attribution." }, "setTitle": { "$ref": "#/$defs/SetTitleEvent", "asCell": [ "stream" ], "description": "Rename the topic. Requires `agentName` and returns the persisted title\nwith its attribution. This direct-address verb stays outside the board's\nnarrow `TopicPiece` demand." }, "startEditBody": { "asCell": [ "stream", "opaque" ], "description": "UI wrapper: open the body editor, seeding the session drafts from the\ndurable body and mention map.", "tier": "wrapper" }, "startEditTitle": { "asCell": [ "stream", "opaque" ], "description": "UI wrapper: open the rename editor, seeding the session draft from the\ndurable title.", "tier": "wrapper" }, "submitComment": { "asCell": [ "stream", "opaque" ], "description": "UI wrapper: append the session comment draft under the viewer's\nProfile. Reads session-local state, so it is a silent no-op headless —\nthe contract verb is `addComment`.", "tier": "wrapper" }, "submitLink": { "asCell": [ "stream", "opaque" ], "description": "UI wrapper: append the session link drafts under the viewer's Profile —\nthe contract verb is `addLink`.", "tier": "wrapper" }, "title": { "default": "", "description": "The topic's title. Defaulted rather than required, like every other\nfield a board card renders: the card list is a mapped sub-pattern, so\nthis type reaches a piece holding topics written before the field\nexisted as the argument schema its update is checked against, and a\nrequired property those topics lack refuses that update outright.\n`deno task pattern-vintage` is what catches it, by replaying a real\ndeployed board.", "type": "string" }, "titleDraft": { "type": "string" }, "titleUpdatedAt": { "type": [ "number", "undefined" ] }, "titleUpdatedBy": { "anyOf": [ { "type": "undefined" }, { "$ref": "#/$defs/TopicAuthor" } ], "description": "Attribution of the last rename; unset until the first `setTitle`.\nBeside `setTitle` rather than on the projection, for the same reason." }, "unmention": { "$ref": "#/$defs/UnmentionEvent", "asCell": [ "stream" ], "description": "Stop referencing a piece: removes every `mention`-made entry naming it.\nReferences made in the prose are retracted by editing the prose, not by\nthis. Takes no `agentName` and returns no value." } }, "required": [ "commentDraft", "bodyDraft", "editingBody", "titleDraft", "editingTitle", "linkUrlDraft", "linkLabelDraft", "linkKindDraft", "referencesDraft", "setTitle", "removeComment", "editComment", "removeLink", "startEditTitle", "saveTitle", "cancelEditTitle", "submitComment", "startEditBody", "saveBody", "cancelEditBody", "submitLink", "$UI", "body", "comments", "links", "mentions", "references", "mentioned", "referencedBy", "addComment", "addLink", "setBody", "mention", "unmention", "$NAME", "title", "createdAt", "commentCount", "lastActivityAt" ], "tags": [ "topic" ], "type": "object" } }