Skip to content

v2: protocol carries no part id; consumer requires one, so text references never reach the jump pipeline #523

Open

Description

@jensenojs

Bug

Jumping to a symbol/file via <CR> silently misses every file mentioned in assistant text but not touched by a tool call. Tool-edited files work; text-only references never reach the jump pipeline.

Cause

The V2 event protocol does not carry a part id for text/reasoning content. A live server test on 2.0.28 (/api/event) shows session.text.ended payloads are { sessionID, assistantMessageID, ordinal, text } — identity is assistantMessageID + ordinal, and no textID/part.id exists on the wire for these parts (tool parts are the exception: session.tool.* carries a real tool id).

The consumer path assumes every part has part.id:

  • lua/opencode/ui/reference_facts.lua M.rebuild gates the whole loop on if part.id (:159) and keys the part_refs cache and reference_parser.clear on part.id. V2 text parts never have one, so they are skipped before collect_part_refs runs.
  • reference_parser.parse_references(part.text, part.id) and the emitted refs carry part_id = part.id.
  • lua/opencode/ui/formatter.lua current_part_text_references likewise requires part.id.

lua/opencode/protocols/v2/observation/messages.lua ordinal_content (:145) builds { kind = kind, text = '' } and only uses ordinal as an internal index key ("text:0", "reasoning:0"); the ordinal is never stored on the content part, so downstream has no way to reconstruct identity either.

This is not a normalize bug: V2 wire events genuinely have no part id for text/reasoning, and V1's content_mappers keeping id = part.id (protocols/v1/normalize.lua:587,598) reflects a V1-only property. The fix belongs in the consumer.

Why it's silent

Tool parts carry id + target.path, so files the assistant edited still reach current_files and drive symbol jump. Only assistant text references are dropped, and both paths share current_files — the broken path is masked by the working tool path. In practice: jumping to a symbol in a file the assistant only mentions (no edit tool call) always fails with No symbol target found.

Fix

Give the V2 consumer a stable per-part identity from the wire data: (assistantMessageID, kind, ordinal).

  • In lua/opencode/protocols/v2/observation/messages.lua, store the ordinal on the content part in ordinal_content (e.g. content.ordinal = data.ordinal), so identity survives normalization.
  • In lua/opencode/ui/reference_facts.lua, derive a synthetic part_id for parts lacking one — message.id .. ':' .. kind .. ':' .. tostring(ordinal) — and use it for the part_refs cache, reference_parser bookkeeping, and the refs' part_id field. Tool parts keep their real id.

V1 is unaffected (it already has part.id). No change to normalize.lua needed.

Activity

  1. self-assigned this
    on Oct 14, 2026
  2. jensenojs commented on Oct 14, 2026

    @jensenojs
    CollaboratorAuthor

    still need code clean..

  3. changed the title [-]v2: assistant text/reasoning parts drop id, text references never reach jump pipeline[/-] [+]v2: protocol carries no part id; consumer requires one, so text references never reach the jump pipeline[/+] on Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions