feat(context): a reply names what it answers #603

Merged
coilyco-ops merged 1 commit from feat/a-reply-names-its-subject into main 2026-08-13 16:57:08 +00:00
Member

closes #579

What was happening

Current() carried only the member's own text. ReferencedMessage was read for the summon check and for trace lookup, and never for the prompt.

So the replied-to message reached the model only if it happened to fall inside the channel history window. The subject of a reply depended on how many messages had arrived since, and disappeared entirely for a reply to anything older than the window — exactly the report: "the harness treats it as if responding to the latest channel message, losing the reply context entirely."

Discord supplies the referenced message inline, so carrying it costs no lookup.

The rendering

alpha is replying to coilysiren: the wipe is scheduled for Tuesday
The request that follows is from alpha.

Both branches of buildTurnContext render it, because a reply can be the first message in an empty window. A turn that is not a reply renders exactly as before, held by a test in both branches.

One thing worth recording

ReplySubject is deliberately not a TranscriptEntry. I wrote it as a self-referential *TranscriptEntry first. It compiles, it passes vet, and then:

panic: AddTool: tool "turn": input schema: ForType(community.TurnInput):
  computing element schema: cycle detected for type community.TranscriptEntry

The MCP tool schema walks TurnInput and cannot express a cycle. I found that by running the tests, not by reasoning about it — nothing in the type system or the linters objects, and the failure is at startup rather than at build.

The flat type is the better shape regardless: a reply target has no provenance of its own and no reply of its own, so Asserted and a nested ReplyTo were fields that could only ever be wrong. It keeps Counterpart, so a reply to this service does not read as a reply to a member.

ward exec gate green.

closes #579 ## What was happening `Current()` carried only the member's own text. `ReferencedMessage` was read for the summon check and for trace lookup, and **never for the prompt**. So the replied-to message reached the model only if it happened to fall inside the channel history window. The subject of a reply depended on **how many messages had arrived since**, and disappeared entirely for a reply to anything older than the window — exactly the report: *"the harness treats it as if responding to the latest channel message, losing the reply context entirely."* Discord supplies the referenced message inline, so carrying it costs no lookup. ## The rendering ``` alpha is replying to coilysiren: the wipe is scheduled for Tuesday The request that follows is from alpha. ``` Both branches of `buildTurnContext` render it, because a reply can be the first message in an empty window. A turn that is not a reply renders exactly as before, held by a test in both branches. ## One thing worth recording `ReplySubject` is deliberately **not** a `TranscriptEntry`. I wrote it as a self-referential `*TranscriptEntry` first. It compiles, it passes `vet`, and then: ``` panic: AddTool: tool "turn": input schema: ForType(community.TurnInput): computing element schema: cycle detected for type community.TranscriptEntry ``` The MCP tool schema walks `TurnInput` and cannot express a cycle. **I found that by running the tests, not by reasoning about it** — nothing in the type system or the linters objects, and the failure is at startup rather than at build. The flat type is the better shape regardless: a reply target has no provenance of its own and no reply of its own, so `Asserted` and a nested `ReplyTo` were fields that could only ever be wrong. It keeps `Counterpart`, so a reply to this service does not read as a reply to a member. `ward exec gate` green.
feat(context): a reply names what it answers
All checks were successful
ci / image-build (pull_request) Successful in 21s
ci / test (pull_request) Successful in 33s
ci / publish-echo-image (pull_request) Has been skipped
ci / publish-observed (pull_request) Has been skipped
a82c4e4193
When a member @-mentions this service in a reply, the turn carried only their
own text. The referenced message reached the model only if it happened to fall
inside the channel history window, so the subject of a reply depended on how
many messages had arrived since, and vanished entirely for a reply to anything
older than the window.

Discord supplies the referenced message inline, so this costs no lookup.

ReplySubject is deliberately not a TranscriptEntry. A self-referential field
compiles and passes vet, and then panics at startup: the MCP tool schema walks
TurnInput and reports a cycle for community.TranscriptEntry. I found that by
running the tests rather than by reasoning about it, and the flat type is the
better shape anyway, since a reply target has no provenance and no reply of
its own.

Both branches of buildTurnContext render it, because a reply can be the first
message in an empty window, and a turn that is not a reply renders exactly as
before.

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
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!603
No description provided.