Weave · ClaudeCodeTranscript ontology · v0.2
Every kind of content one session can hold — text, thinking, tool calls, tool results, images, attachments, compaction breaks — as a single tree. A transcript is a Transcript that hasEvent many Events; a message is a Turn (one kind of Event) built from ordered content blocks. Everything else is a sibling Event type. Counts (n≈) are real, from the 125-session corpus survey the ontology was carved against.
The conversation as it happened. Turns + their content blocks, chained by reply-to.
Everything else the harness records: attachments, system events, compaction, titles.
What a detector found — an oa:Annotation anchored to a span. Added, never in the raw log.
A finding as an unasserted RDF-1.2 triple term — "we believe X", not "X is true".
wired the adapter emits this today · designed in the ontology, not yet wired in the Rust adapter (still turns-only for the spine + emojikey reader).
A Turn is one message. Its message.content[] is an
ordered list of blocks (order is meaningful: text → tool_use → text…). Each block is a
subtype of ContentBlock. This is where all the "kinds of content" you asked about live.
Transcript — one .jsonl session wired ├─ weave:sessionSlug "820a266b-…" │ └─ weave:hasEvent ─→ Turn (one message · an Event subtype) wired ├─ rdf:type weave:Turn (→ UserTurn | AssistantTurn) ├─ weave:role "assistant" ├─ weave:timestamp "2026-06-30T17:54:44Z"^^xsd:dateTime ← TIME anchor (Pool join) ├─ weave:parentEvent ─→ …/Turn/<prev> ← reply-to spine ├─ weave:model "claude-opus-4-8" (AssistantTurn only) designed │ └─ weave:hasContentBlock ─→ [ ordered blocks ] blockOrder 0,1,2… designed │ ├─◆ TextBlock weave:text "…prose readers detect over…" n≈8940 │ ├─◆ ThinkingBlock {thinking, signature} extended-thinking trace n≈75 │ ├─◆ ToolUseBlock {id, name, input, caller} an assistant tool call n≈11320 │ └─ pairs by tool_use_id ↓ ├─◆ ToolResultBlock {tool_use_id, content, is_error} the result n≈11320 │ └─◆ ImageBlock source{type, media_type, data} n≈16 ↑ FEDERATION HOOK — pasted/produced images are the Pool-Moment join surface
tool_use_id —
often in different turns (the call is the assistant's, the result comes back on the next line).
A reader that wants "what did this tool do" walks that id, not the tree.
A Turn is just one kind of Event. The harness writes many others as sibling
weave:Events. The big one is Attachment — not one envelope but a
tag-union of 20 subtypes, carved three ways by whether the payload carries a graph edge.
Transcript weave:hasEvent ─→ Event (siblings of Turn) │ ├─ Attachment type=attachment · a 20-way tag union n≈2289 designed │ │ │ ├─ (A) EDGE-BEARING → typed subclass (a reader TRAVERSES the edge) │ │ ├─◆ QueuedCommandAttachment origin{kind,server} ← where a prompt came from; kind=channel → a subtext peer │ │ ├─◆ HookContextAttachment memory-recall payload → the Weave⋈Soul join surface │ │ ├─◆ NestedMemoryAttachment CLAUDE.md path → edge into the soul-repo graph │ │ ├─◆ FileRefAttachment {file, edited_text_file, compact_file_reference} → a filesystem/Pool-blob node │ │ ├─◆ InvokedSkillsAttachment → edge to Skill objects (first-class in the kit) │ │ └─◆ HookExecAttachment {hook_success, hook_non_blocking_error} → a hook run │ │ │ ├─ (B) VALUE-ONLY → ONE class, discriminator + payload (just READ) │ │ └─◆ StateAttachment attachmentKind ∈ {task_reminder, date_change, plan_mode, │ │ ultrathink_effort, command_permissions, skill_listing …} │ │ │ └─ (C) REGISTRY DELTAS → ONE class, parametrized by registryName │ └─◆ RegistryDelta registryName ∈ {tools, mcp, agents} + added/removedNames │ ├─ SystemEvent subtype ∈ {turn_duration, compact_boundary, hook, api_error} n≈1666 ├─ FileHistorySnapshot editor undo state — not conversation n≈4035 ├─ QueueOperation prompt-queue mechanics n≈2416 ├─ PermissionModeEvent ∈ {normal, plan, acceptEdits, bypassPermissions} n≈1407 ├─ LastPromptEvent n≈1330 ├─ ProgressEvent n≈385 ├─ ModeEvent n≈789 ├─ AiTitleEvent n≈699 ├─ PrLinkEvent n≈94 └─ CustomTitleEvent n≈14 · AgentNameEvent n≈14 · SummaryEvent
prov:Activity, not a mere event —
and it's derivable, not guessed: it prov:used the pre-compact tail
and prov:generated the continuation (linked by logicalParentUuid),
marked by system.subtype=compact_boundary (n=168 verified).
preTokens→postTokens is a load/biorhythm signal.
Nothing above 03 is in the raw log's meaning-layer — it's structure. A reader walks
the spine and mints an oa:Annotation (standard Web Annotation vocab) plus an
unasserted RDF-1.2 triple-term claim. This is the emojikey reader, live today
(wired).
Turn (assistant, from the spine ↑) └─◆ oa:Annotation — one finding wired ├─ prov:wasAttributedTo ─→ detector/emojikey WHO found it ├─ prov:generatedAtTime "2026-06-30T17:54:44Z"^^xsd:dateTime ├─ weave:eventType "emojikey/harvest" ├─ weave:sourceKind "tool_result" authored (live) vs quoted │ ├─ oa:hasTarget ─→ ◆ target WHERE in the source │ ├─ oa:hasSource ─→ transcript/820a266b… (the whole doc) │ ├─ oa:hasSelector ─→ ◆ TextPositionSelector │ │ ├─ oa:start 2228^^xsd:integer │ │ └─ oa:end 3277^^xsd:integer │ └─ weave:atEvent ─→ urn:soul:SG:Weave/Turn/572a… the EXACT turn (soul anchor) │ ├─ prov:used ─→ urn:soul:SG:Weave/Turn/572a… the evidence it read │ └─ oa:hasBody ─→ ◆ body WHAT the signal says ├─ weave:sig_me "📏🌊5∠90…" ├─ weave:sig_content "🧠🎨…" ├─ weave:sig_you "🤝…" └─ weave:sig_<k> …numeric legs → xsd:integer/decimal (FILTER-able) claim/0470b23… — the finding as a BELIEF, not a fact wired ├─ rdf:reifies <<( …Turn/572a… weave:exhibits "emojikey/harvest" )>> │ the parens = UNASSERTED. "we believe this turn exhibits X", never "X is true". ├─ prov:wasDerivedFrom ─→ oa:Annotation belief traces to its evidence ├─ weave:detector "emojikey" └─ weave:detectionType "emojikey/harvest"