feat(telemetry): place a turn by putting Discord's identifiers on its span #342

Merged
coilysiren merged 2 commits from feat/discord-identifiers-on-the-turn-span into main 2026-08-13 10:36:14 +00:00
Member

closes #337

A trace id is only worth handing a member if the turn it names can be found. The turn span now carries the author's account id, the guild, the channel, the thread when there is one, and the member's message id — under the same keys the send span already used, so one query returns both halves of an exchange rather than two shapes joined by hand.

The one part that is not mechanical

Discord models a thread as a channel. The obvious mapping reports the thread id as discord.channel.id, and every thread turn then vanishes from a query for the channel it hangs under — which is the query an operator actually runs. So the parent is reported as the channel and the thread gets its own key.

Resolution reads cached gateway state and never calls the Discord API. An unresolved channel reports itself as a channel, which is what it is, and contributes no thread id. Absent beats blank.

The account id is a reversal, and should be read as one

The code said, in a comment beside this exact span, that the requester was deliberately not a span attribute because an account id is not operational telemetry. That was a real position, not an oversight.

It was overturned on purpose, by the director, in the issue. The reasoning that changed: an account id in a private telemetry backend and a handle in a public reply are different objects, and the job-record indirection that stood in for it does not cover a turn that failed before a job existed.

Both statements of the old position — the code comment and the AGENTS.md safety sentence — are corrected here rather than left contradicting the code.

What did not move

Prompt, model, tool, and reply bodies stay out. Nothing member-visible goes in: no display name, no nickname, no handle. A direct message contributes nothing at all, because a DM never enters the turn logger and this is not the change that should be its first exception. The HTTP turn does not implement the interface, and a test holds it to that.

Worth knowing, filed separately

ward exec gate reported PASS on this tree while a new untracked file carried a comment violation. Pre-commit only saw it once it was staged. That is the same class of surprise as #305.

ward exec gate green: build, policy-check, vet, test, test-skips, pre-commit.

closes #337 A trace id is only worth handing a member if the turn it names can be found. The turn span now carries the author's account id, the guild, the channel, the thread when there is one, and the member's message id — under the same keys the send span already used, so one query returns both halves of an exchange rather than two shapes joined by hand. ## The one part that is not mechanical Discord models a thread as a channel. The obvious mapping reports the thread id as `discord.channel.id`, and every thread turn then vanishes from a query for the channel it hangs under — which is the query an operator actually runs. So the **parent** is reported as the channel and the thread gets its own key. Resolution reads cached gateway state and **never calls the Discord API**. An unresolved channel reports itself as a channel, which is what it is, and contributes no thread id. Absent beats blank. ## The account id is a reversal, and should be read as one The code said, in a comment beside this exact span, that the requester was *deliberately* not a span attribute because an account id is not operational telemetry. That was a real position, not an oversight. It was overturned on purpose, by the director, in the issue. The reasoning that changed: an account id in a private telemetry backend and a handle in a public reply are different objects, and the job-record indirection that stood in for it does not cover a turn that failed before a job existed. Both statements of the old position — the code comment and the `AGENTS.md` safety sentence — are corrected here rather than left contradicting the code. ## What did not move Prompt, model, tool, and reply bodies stay out. Nothing member-visible goes in: no display name, no nickname, no handle. **A direct message contributes nothing at all**, because a DM never enters the turn logger and this is not the change that should be its first exception. The HTTP turn does not implement the interface, and a test holds it to that. ## Worth knowing, filed separately `ward exec gate` reported PASS on this tree while a new **untracked** file carried a comment violation. Pre-commit only saw it once it was staged. That is the same class of surprise as https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/305. `ward exec gate` green: build, policy-check, vet, test, test-skips, pre-commit.
feat(telemetry): place a turn by putting Discord's identifiers on its span
All checks were successful
ci / test (pull_request) Successful in 35s
ci / publish-echo-image (pull_request) Has been skipped
ci / publish-observed (pull_request) Has been skipped
ci / image-build (pull_request) Successful in 19s
df582b33d6
A trace id is only worth handing a member if the turn it names can be found.
The turn span now carries the author's account id, the guild, the channel, the
thread when there is one, and the member's message id, under the same keys the
send span already used, so one query returns both halves of an exchange.

Threads are the part that is not mechanical. Discord models a thread as a
channel, so the obvious mapping reports the thread id as the channel id and
every thread turn vanishes from a query for the channel it hangs under, which
is the query an operator runs. The parent is reported as the channel and the
thread gets its own key. Resolution reads cached gateway state and never calls
the Discord API. An unresolved channel reports itself as a channel, which is
what it is, and contributes no thread id.

The account id is a reversal, not an addition. The code said in a comment
beside this span that the requester was deliberately not a span attribute
because an account id is not operational telemetry. That was a real position.
It was overturned on purpose in the issue, by the director, on the grounds
that an account id in a private backend and a handle in a public reply are
different objects. The comment and the AGENTS.md sentence that asserted the
old position are corrected in this commit rather than left contradicting the
code.

What did not move: prompt, model, tool, and reply bodies stay out, nothing
member-visible goes in, and a direct message contributes nothing at all,
because a DM never enters the turn logger and this is not the change that
should be its first exception.

closes #337

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Merge branch 'main' into feat/discord-identifiers-on-the-turn-span
All checks were successful
ci / test (pull_request) Successful in 30s
ci / publish-echo-image (pull_request) Has been skipped
ci / publish-observed (pull_request) Has been skipped
ci / image-build (pull_request) Successful in 20s
7b48e5038d
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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!342
No description provided.