Skip to content

Show a merged game's addresses on the page its slug redirects to - #190

Merged
HarryCordewener merged 1 commit into
mainfrom
claude/merged-endpoints-on-winner
Sep 25, 2026
Merged

HarryCordewener merged 1 commit into
mainfrom
claude/merged-endpoints-on-winner

Conversation

@HarryCordewener

@HarryCordewener HarryCordewener commented Sep 18, 2026 •

Copy link
Copy Markdown
Member

Part of #188. The ChatMUD merge that closes it waits on this landing.

The defect

A merge says two listings are one game, so every address either answered at is an address of that game. The absorbed game keeps its endpoint rows — nothing moves, and the merge stays a redirect — but the page its slug 301s to read only the winner's own endpoints (NpgsqlEndpointStore.ForGameAsync(row.Id)). The loser's addresses went on being crawled and were shown nowhere a reader could reach.

Found about to run game_merge chatmud ← chatmud-2. chatmud.com:7443, the TLS door #184 discovered, is the listing being absorbed. The merge would have taken the one new fact about ChatMUD off the only page left. It's probably not the only case: "two ports of one game" is the commonest reason to merge, and this also un-hides whatever earlier merges are hiding. I couldn't measure how many from here, because it needs a merge_log join the MCP tools don't expose.

The fix

ForListingAsync is the union: the game's own endpoints, plus those of any game with an un-reverted merge_log row pointing here. One hop is the whole walk, because merge_log_no_chains forbids a game that is both absorbed and absorbing. A reverted merge is two games again, so its addresses stay on their own page.

Deliberately not ForGameAsync, and deliberately not on IEndpointStore. That method also answers IdentityMatcher and DnsClaim, where "this game's addresses" has to mean the ones it answered at itself. Widening it would let a DNS claim verified against the winner vouch for an absorbed game's hosts: a change to who can prove ownership of what, made as a side effect of a display fix. Because the union exists only on the concrete store, nothing that consumes the interface can reach it.

Testing

Two new Postgres tests in MergedGameVisibilityPostgresTests. The winner's page lists both addresses, with the TLS door still TlsMeasured. A reverted merge keeps them apart: that test passed before the change, so I verified it by mutation (dropping reverted_at IS NULL fails it). 3,208 pass across all six suites.

After this lands

game_merge winner=chatmud loser=chatmud-2, with the evidence from #188 as the because: same host, NAME = ChatMUD on both, the same count, a byte-identical connect screen.

The other half of #188 needs no change. 110-10-160-150-4001 is a real Dead Souls game on Intermud-3 whose announced port serves its owner's Synology NAS admin page, so it stays listed (comment).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Winner game pages now display endpoints retained from actively merged games.
    • Endpoint types remain preserved when merged-game addresses appear on the winner’s page.
  • Bug Fixes

    • Reverted merges keep absorbed-game endpoints separate and out of the winner’s listing.

A merge says two listings are one game, so every address either answered at is
an address of that game. The absorbed game keeps its endpoint rows — nothing
moves, and the merge stays a redirect — but the page its slug 301s to read only
the winner's own endpoints. The loser's addresses went on being crawled and were
shown nowhere a reader could reach.

Found about to merge ChatMUD's two listings for #188: `chatmud.com:7443`, the
TLS door the crawl discovered yesterday, belongs to the listing being absorbed.
The merge would have taken it off the only page left. It is probably not the
only one — "two ports of one game" is the commonest reason to merge — and this
un-hides whatever earlier merges are hiding too.

`NpgsqlEndpointStore.ForListingAsync` is the union, one hop through `merge_log`
(`merge_log_no_chains` makes one hop the whole walk), and a reverted merge gives
the addresses back. It is deliberately not `ForGameAsync` and deliberately not on
`IEndpointStore`: that method also answers `IdentityMatcher` and `DnsClaim`,
where "this game's addresses" has to mean the ones it answered at itself.
Widening it would let a DNS claim verified against the winner vouch for an
absorbed game's hosts — a change to who can prove ownership of what, made as a
side effect of a display fix. Living only on the concrete store keeps it out of
reach of everything that consumes the interface.

3,208 tests pass across all six suites. The reverted-merge test passed before the
change and was verified by mutation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Essentials

Run ID: d33afd85-989a-4bff-9701-efc04d9acc2b

📥 Commits

Reviewing files that changed from the base of the PR and between 1d62f79 and eeec55f.

📒 Files selected for processing (4)
  • CLAUDE.md
  • src/MUI.Catalog/Persistence/Games/NpgsqlEndpointStore.cs
  • src/MUI.Catalog/Persistence/Queries/NpgsqlGameQueries.GamePage.cs
  • tests/MUI.Catalog.Tests/Persistence/Games/MergedGameVisibilityPostgresTests.cs

Included review availability: 0 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 2 reviews per hour.


Walkthrough

The change adds listing-scoped endpoint retrieval for active merged games. Game pages use this retrieval path. PostgreSQL tests cover endpoint visibility after merges and reverted merges. Documentation records the distinction from game-scoped retrieval.

Changes

Merged game endpoint visibility

Layer / File(s) Summary
Listing endpoint query
src/MUI.Catalog/Persistence/Games/NpgsqlEndpointStore.cs, CLAUDE.md
ForListingAsync returns endpoints for the requested game and games actively merged into it. Results are ordered by host and port. The documentation records this behavior.
Game page integration
src/MUI.Catalog/Persistence/Queries/NpgsqlGameQueries.GamePage.cs
PageAsync uses ForListingAsync to load endpoints for the game page.
Merge visibility validation
tests/MUI.Catalog.Tests/Persistence/Games/MergedGameVisibilityPostgresTests.cs
PostgreSQL tests verify endpoint type preservation after a merge and endpoint separation after a reverted merge.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant GamePage
  participant NpgsqlEndpointStore
  participant PostgreSQL
  GamePage->>NpgsqlEndpointStore: ForListingAsync(gameId)
  NpgsqlEndpointStore->>PostgreSQL: Load endpoints for the listing and active merged games
  PostgreSQL-->>NpgsqlEndpointStore: Endpoint rows
  NpgsqlEndpointStore-->>GamePage: GameEndpoint records
Loading

Merge Risk: ⚪ Minimal · up to eeec5

Active merged endpoints appear on the winner’s page, while reverted merges remain separate. No actionable merge risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 3 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: showing addresses from a merged game on the page to which its slug redirects.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 9 functions across 3 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@HarryCordewener
HarryCordewener merged commit 0df4178 into main Sep 25, 2026
3 checks passed
@HarryCordewener
HarryCordewener deleted the claude/merged-endpoints-on-winner branch September 25, 2026 03:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant