A hostname with no scheme is not a link, so a member's name inside it is still rewritten and still pings them #481

Closed
opened 2026-08-13 14:19:24 +00:00 by coilyco-ops · 1 comment
Member

Filed by Angie (ENG) · seat claude-macos-…-ee99, splitting the residual out of #468 rather than widening that issue past what it reported.

#472 fixed the reported case. This is the part it does not reach.

Measured on 550205e, which carries 472

in ="See https://eco-app.coilysiren.me/jobs."      out= unchanged
in ="See https://eco-app.coilysiren.me for jobs"   out= unchanged
in ="See [jobs](https://eco-app.coilysiren.me/jobs)" out= unchanged
in ="See <https://eco-app.coilysiren.me/jobs>"     out= unchanged
in ="See eco-app.coilysiren.me/jobs"               out="See eco-app.<@1024000000000000001>.me/jobs"

Roster of one, coilysiren. Four of five shapes survive. The fifth is the one with no scheme.

Why the fix stops there, and it is not a flaw in the fix

mentionSpans splits on urlSpan, which is https?://[^\s<>()\[\]]+. That is deliberate and correct. urlSpan is the same expression the prose validators mask with, and having one definition of where a link starts is the property #465 established. A bare host simply is not a link by that definition, so the span never covers it.

So the fix is not to widen urlSpan. Widening it would change what the grounding and neutral-style validators mask out, which is a safety surface, to repair a mention bug. Two guards, one expression, and only one of them wants the change.

Two harms, and the second is the one that matters

The text is mangled. eco-app.<@1024000000000000001>.me/jobs is not an address anyone can use, and the member is left with no way to reach the page they were pointed at.

It pings. agent.go sends the resolved ids as AllowedMentions.Users, so this is not a cosmetic rewrite. A member whose display name happens to be a label in a hostname gets a real Discord notification because their name appeared in a URL. That is the harm worth fixing promptly.

The shape I propose

Not a link rule, a name rule. A display name that sits inside a dotted identifier is a label rather than a person:

  • immediately preceded by ., or
  • immediately followed by . and then a letter or digit

The second condition is what separates coilysiren.me/jobs at the start of a sentence from Thanks, coilysiren. at the end of one. A trailing dot followed by a space or the end of the reply is punctuation, not a domain.

This needs the resolver to consider later matches in a span rather than only the first, since the first occurrence may be the one inside the hostname and a real prose mention may follow it.

Acceptance

  • Every shape in the table above is unchanged, including the schemeless one.
  • A name at the end of a sentence still resolves, so the fix does not buy this by refusing punctuation.
  • A reply where the hostname occurrence comes first and a prose mention comes second resolves the prose one.
  • No change to urlSpan and therefore none to what the prose validators mask.

One adjacent case I am deliberately not fixing

A name matching part of a hyphenated label, such as a member called app in eco-app.example.com, is rewritten by the same path. That is a substring-of-a-word problem rather than a dotted-identifier one, the existing boundary class already treats - as a separator on purpose, and I would rather not widen it on speculation. Say so on this issue and I will take it as a follow-up.

Claiming this, since I found it and have the measurement loaded. 20 minutes from this timestamp.

Filed by Angie (ENG) · seat `claude-macos-…-ee99`, splitting the residual out of https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/468 rather than widening that issue past what it reported. https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/pulls/472 fixed the reported case. This is the part it does not reach. ## Measured on `550205e`, which carries 472 ``` in ="See https://eco-app.coilysiren.me/jobs." out= unchanged in ="See https://eco-app.coilysiren.me for jobs" out= unchanged in ="See [jobs](https://eco-app.coilysiren.me/jobs)" out= unchanged in ="See <https://eco-app.coilysiren.me/jobs>" out= unchanged in ="See eco-app.coilysiren.me/jobs" out="See eco-app.<@1024000000000000001>.me/jobs" ``` Roster of one, `coilysiren`. Four of five shapes survive. The fifth is the one with no scheme. ## Why the fix stops there, and it is not a flaw in the fix `mentionSpans` splits on `urlSpan`, which is `https?://[^\s<>()\[\]]+`. That is deliberate and correct. `urlSpan` is the same expression the prose validators mask with, and having one definition of where a link starts is the property https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/465 established. A bare host simply is not a link by that definition, so the span never covers it. **So the fix is not to widen `urlSpan`.** Widening it would change what the grounding and neutral-style validators mask out, which is a safety surface, to repair a mention bug. Two guards, one expression, and only one of them wants the change. ## Two harms, and the second is the one that matters **The text is mangled.** `eco-app.<@1024000000000000001>.me/jobs` is not an address anyone can use, and the member is left with no way to reach the page they were pointed at. **It pings.** `agent.go` sends the resolved ids as `AllowedMentions.Users`, so this is not a cosmetic rewrite. A member whose display name happens to be a label in a hostname gets a real Discord notification because their name appeared in a URL. That is the harm worth fixing promptly. ## The shape I propose Not a link rule, a **name** rule. A display name that sits inside a dotted identifier is a label rather than a person: - immediately preceded by `.`, or - immediately followed by `.` and then a letter or digit The second condition is what separates `coilysiren.me/jobs` at the start of a sentence from `Thanks, coilysiren.` at the end of one. A trailing dot followed by a space or the end of the reply is punctuation, not a domain. This needs the resolver to consider later matches in a span rather than only the first, since the first occurrence may be the one inside the hostname and a real prose mention may follow it. ## Acceptance - Every shape in the table above is unchanged, including the schemeless one. - A name at the end of a sentence still resolves, so the fix does not buy this by refusing punctuation. - A reply where the hostname occurrence comes first and a prose mention comes second resolves the prose one. - No change to `urlSpan` and therefore none to what the prose validators mask. ## One adjacent case I am deliberately not fixing A name matching part of a **hyphenated** label, such as a member called `app` in `eco-app.example.com`, is rewritten by the same path. That is a substring-of-a-word problem rather than a dotted-identifier one, the existing boundary class already treats `-` as a separator on purpose, and I would rather not widen it on speculation. Say so on this issue and I will take it as a follow-up. **Claiming this**, since I found it and have the measurement loaded. 20 minutes from this timestamp.
Author
Member

Fixed. Pull request #484, ward exec gate green, carrying closes #481.

Correction first, because it is in the commit message and I cannot amend a pushed commit. The body says "Pull request 418 taught the resolver to skip link spans". That is wrong. It was #472. 418 belongs to the reply-budget thread and has nothing to do with mentions. This comment is the correction of record.

What shipped

A name rule rather than a link rule, exactly as scoped above. urlSpan is untouched, so nothing changed about what the grounding and neutral-style validators mask.

resolveWithin now reads every occurrence in a span rather than the first, which is what makes the hostname-then-person sentence work.

Acceptance, each with a case

Criterion Case
Every shape unchanged, including schemeless TestASchemelessHostnameIsNotAMention
A name ending a sentence still resolves TestANameEndingASentenceStillResolves
Hostname first, prose mention second TestAHostnameDoesNotConsumeTheLaterProseMention
The 472 shapes stay fixed TestEveryLinkShapeStaysUntouched
The boundary itself TestInDottedIdentifierSeparatesDomainsFromPunctuation

Verified by reverting the fix, not by watching it pass

I stubbed inDottedIdentifier to return false and re-ran, so the cases are known to be load-bearing rather than decorative:

--- FAIL: TestASchemelessHostnameIsNotAMention
    "See eco-app.coilysiren.me/jobs" pinged [1024000000000000001] for a hostname label
    "coilysiren.me/jobs has the list" pinged [1024000000000000001] for a hostname label
    "Both eco-app.coilysiren.me and forgejo.coilysiren.me are up" pinged [...]
    "Mail is at kai@eco-app.coilysiren.me today" pinged [...]
--- FAIL: TestAHostnameDoesNotConsumeTheLaterProseMention
--- FAIL: TestInDottedIdentifierSeparatesDomainsFromPunctuation

TestANameEndingASentenceStillResolves passes in both states, which is correct and worth naming: it is a non-regression guard rather than a demonstration of the fix. Its job is to fail if someone later makes the rule greedier.

Two cases I added beyond the acceptance

Both came from asking what else is dot-adjacent rather than from the report:

  • Two hosts in one sentence. The loop terminates on the first acceptable match, so a second hostname had to be checked rather than assumed.
  • An email address. kai@eco-app.coilysiren.me is covered by the same rule, which I had not predicted and is worth having pinned.

Also pinned a multibyte case, name.école, since the rule reads the rune after the dot rather than the byte.

Still not fixed, unchanged from the issue body: a name matching part of a hyphenated label. Recorded rather than widened.

**Fixed. Pull request https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/pulls/484, `ward exec gate` green, carrying `closes #481`.** **Correction first, because it is in the commit message and I cannot amend a pushed commit.** The body says *"Pull request 418 taught the resolver to skip link spans"*. That is wrong. It was https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/pulls/472. 418 belongs to the reply-budget thread and has nothing to do with mentions. This comment is the correction of record. ## What shipped A name rule rather than a link rule, exactly as scoped above. `urlSpan` is untouched, so nothing changed about what the grounding and neutral-style validators mask. `resolveWithin` now reads every occurrence in a span rather than the first, which is what makes the hostname-then-person sentence work. ## Acceptance, each with a case | Criterion | Case | | --- | --- | | Every shape unchanged, including schemeless | `TestASchemelessHostnameIsNotAMention` | | A name ending a sentence still resolves | `TestANameEndingASentenceStillResolves` | | Hostname first, prose mention second | `TestAHostnameDoesNotConsumeTheLaterProseMention` | | The 472 shapes stay fixed | `TestEveryLinkShapeStaysUntouched` | | The boundary itself | `TestInDottedIdentifierSeparatesDomainsFromPunctuation` | ## Verified by reverting the fix, not by watching it pass I stubbed `inDottedIdentifier` to return `false` and re-ran, so the cases are known to be load-bearing rather than decorative: ``` --- FAIL: TestASchemelessHostnameIsNotAMention "See eco-app.coilysiren.me/jobs" pinged [1024000000000000001] for a hostname label "coilysiren.me/jobs has the list" pinged [1024000000000000001] for a hostname label "Both eco-app.coilysiren.me and forgejo.coilysiren.me are up" pinged [...] "Mail is at kai@eco-app.coilysiren.me today" pinged [...] --- FAIL: TestAHostnameDoesNotConsumeTheLaterProseMention --- FAIL: TestInDottedIdentifierSeparatesDomainsFromPunctuation ``` `TestANameEndingASentenceStillResolves` passes in both states, which is correct and worth naming: it is a non-regression guard rather than a demonstration of the fix. Its job is to fail if someone later makes the rule greedier. ## Two cases I added beyond the acceptance Both came from asking what else is dot-adjacent rather than from the report: - **Two hosts in one sentence.** The loop terminates on the first acceptable match, so a second hostname had to be checked rather than assumed. - **An email address.** `kai@eco-app.coilysiren.me` is covered by the same rule, which I had not predicted and is worth having pinned. Also pinned a multibyte case, `name.école`, since the rule reads the rune after the dot rather than the byte. Still not fixed, unchanged from the issue body: a name matching part of a hyphenated label. Recorded rather than widened.
Sign in to join this conversation.
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#481
No description provided.