feat(context): name the message a reply is addressing #617

Closed
coilyco-ops wants to merge 1 commit from coilyco-ops/reply-addresses-a-message into main AGit
Member

closes #579 - a reply reached the model only if the message it addressed happened to fall inside the history window. Older than that and the turn's subject became whatever was said most recently.

closes #579 - a reply reached the model only if the message it addressed happened to fall inside the history window. Older than that and the turn's subject became whatever was said most recently.
feat(context): name the message a reply is addressing
All checks were successful
ci / image-build (pull_request) Successful in 22s
ci / test (pull_request) Successful in 35s
ci / publish-echo-image (pull_request) Has been skipped
ci / publish-observed (pull_request) Has been skipped
efeb15468c
Current() was built from the incoming payload alone and carried nothing about
the reply, so the addressed message reached the model only if it happened to
fall inside the history window, positioned as one ordinary line among others. A
reply to anything older than the window did not reach the model at all, and the
turn's subject became whatever was said most recently.

The harness already read ReferencedMessage twice, for summon detection and for
trace lookup, whose comment states this issue's argument in another context: the
id a member wants is usually in the notice they replied to rather than in what
they typed. It applied that to trace ids and to summons, and not to the subject.

Three shape decisions, recorded on the issue before building.

Named rather than adjacent. Injecting the addressed message as the last history
entry is cheaper and is also indistinguishable from coincidence, because the
model cannot tell an entry placed there deliberately from one that merely
happened to be recent. That is the same defect wearing a different hat.

Supplements recency rather than replacing it. A member who replies to an old
message and then asks something unrelated is real, and promoting the reference
to sole subject would answer the wrong question for them confidently.

Resolved before the turn runs, because Current takes no context. The Gateway
delivers the reference inline for most replies and that costs nothing. When it
does not, which is likeliest for exactly the old messages this is worth most
for, the fetch draws on the same lookup budget as the other gate-forced REST
calls. A reference that cannot be read is an ordinary message, not a failure.

ReplyTo is a ReplyReference rather than a nested TranscriptEntry, because that
type generates the MCP turn tool's input schema and a self-referential type is a
cycle the generator refuses. The existing MCP tests caught that, not review.

The rendered prompt snapshots do not move. This is turn content.

closes #579

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Author
Member

Closing as superseded. Angie (ENG), and it was my pull request. Third duplicate build today.

Another seat landed #579 on main while this sat in the lane. We reached the same core conclusion independently, including the non-obvious one:

// main
// ReplySubject is what a reply answers. Deliberately not a TranscriptEntry: a
// self-referential field is a cycle the MCP tool schema cannot express.

// this pull request
// ReplyReference is the message a turn replies to. It is a distinct type rather
// than a nested TranscriptEntry because that type generates the MCP tool schema.

Two seats hitting the same schema cycle and writing nearly the same comment about it is a decent sign the constraint is real.

Theirs is better in two places

It names the speaker. bob is replying to alice: ... against my Replying to alice: .... In a multi-party channel that matters, and I did not think of it.

It handles an unnamed author. if author == "" { author = "an earlier message" }. Mine rendered nothing in that case.

Theirs is on main, so theirs wins. No argument from me.

Two things mine had that theirs does not, and one of them matters

The truncated reference is not resolved. replyTarget returns nil when ReferencedMessage is nil:

if message == nil || message.ReferencedMessage == nil {
    return nil
}

Discord does not always deliver the referenced message inline, and it is least likely to for old messages, which is exactly the case #579 was filed about: "If the member replies to something older than the window, it does not reach the model at all." That case is still not reached. Mine fell back to a fetch under the existing a.lookups budget.

A replied-to attachment is invisible. ReplySubject carries Author, Content, and Counterpart but no Attachments, so replying to an image renders as replying to empty text. TranscriptEntry carries attachments precisely because silence there reads as a text-only message.

Filing both as a follow-on rather than reopening 579, since 579's headline is genuinely fixed.

On the duplication

Three today, all mine, all on issues that were open and unclaimed when I started: #560, this one, and the pair recorded on #552. In each case both seats followed the claim protocol and it did not help, because the work began before either claim existed. Adding the third instance to 552.

**Closing as superseded. Angie (ENG), and it was my pull request. Third duplicate build today.** Another seat landed https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/579 on `main` while this sat in the lane. We reached the same core conclusion independently, including the non-obvious one: ```go // main // ReplySubject is what a reply answers. Deliberately not a TranscriptEntry: a // self-referential field is a cycle the MCP tool schema cannot express. // this pull request // ReplyReference is the message a turn replies to. It is a distinct type rather // than a nested TranscriptEntry because that type generates the MCP tool schema. ``` Two seats hitting the same schema cycle and writing nearly the same comment about it is a decent sign the constraint is real. ## Theirs is better in two places **It names the speaker.** `bob is replying to alice: ...` against my `Replying to alice: ...`. In a multi-party channel that matters, and I did not think of it. **It handles an unnamed author.** `if author == "" { author = "an earlier message" }`. Mine rendered nothing in that case. Theirs is on `main`, so theirs wins. No argument from me. ## Two things mine had that theirs does not, and one of them matters **The truncated reference is not resolved.** `replyTarget` returns nil when `ReferencedMessage` is nil: ```go if message == nil || message.ReferencedMessage == nil { return nil } ``` Discord does not always deliver the referenced message inline, and it is least likely to for **old** messages, which is exactly the case https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/579 was filed about: *"If the member replies to something older than the window, it does not reach the model at all."* That case is still not reached. Mine fell back to a fetch under the existing `a.lookups` budget. **A replied-to attachment is invisible.** `ReplySubject` carries `Author`, `Content`, and `Counterpart` but no `Attachments`, so replying to an image renders as replying to empty text. `TranscriptEntry` carries attachments precisely because silence there reads as a text-only message. Filing both as a follow-on rather than reopening 579, since 579's headline is genuinely fixed. ## On the duplication Three today, all mine, all on issues that were open and unclaimed when I started: https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/560, this one, and the pair recorded on https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/552. In each case both seats followed the claim protocol and it did not help, because the work began before either claim existed. Adding the third instance to 552.
coilyco-ops closed this pull request 2026-08-13 17:34:37 +00:00
All checks were successful
ci / image-build (pull_request) Successful in 22s
ci / test (pull_request) Successful in 35s
ci / publish-echo-image (pull_request) Has been skipped
ci / publish-observed (pull_request) Has been skipped

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-gaming/sirens-echo!617
No description provided.