Skip to content

Park the one-time importer, and make the backfill seed-only - #11

Merged
HarryCordewener merged 2 commits into
feat/real-data-and-referral-crawlfrom
chore/park-the-importer
Jul 31, 2026
Merged

Park the one-time importer, and make the backfill seed-only#11
HarryCordewener merged 2 commits into
feat/real-data-and-referral-crawlfrom
chore/park-the-importer

Conversation

@HarryCordewener

@HarryCordewener HarryCordewener commented Jul 31, 2026

Copy link
Copy Markdown
Member

Stacked on #9 — review that first; this branch is #9 plus two commits.

1. The importer is not carried on main

The backfill exists to prime one deployment once. Carrying its machinery here means four HTML parsers for third-party sites we intend never to fetch again, compiled and tested in CI for ever. A parser that never runs but still compiles is worse than no parser: it rots silently and reads as a supported feature.

src/MUI.Import, tests/MUI.Import.Tests and tools/live-tintin-import → the local import/one-time branch, which also carries everything from the now-closed #10. PR #10 is closed and its remote branch deleted, so nothing importer-shaped is on GitHub.

2. The backfill takes addresses, and nothing else

Imported values are gone, and with them everything that carried them: FieldSource.ImportedMeasured/ImportedAsserted, IntervalOrigin.ImportedMeasured, the import_provenance table, ArchivePolicy's half-weight credit for a third party's reachable history, and the two-tier §7.6 that justified it.

Three reasons, strongest first:

  • A game's origin is not one fact. The catalogue gets cross-checked against several directories, and any game worth listing appears in more than one — so "imported from MudStats" names whichever fetch ran first, not the game. A provenance chip saying it would present an accident as a fact.
  • That a game exists is public information. Recording where we read a published address adds nothing a reader can use, and it republishes the part of somebody's work with the least claim to be ours.
  • The point is to start with a lot of games and then gather our own data. A history import would fill the heatmaps of exactly the games another directory was already watching, indistinguishable from our own measurement without reading the fine print.

What it costs, in the spec rather than glossed: every game starts at the archive floor on the day we find it, every heatmap starts empty, and the day-one site knows each game's address plus one probe's worth of everything else. That is the intended shape.

IntervalOrigin survives as a one-member enum, and availability_interval.origin as a column: if another party's measurements are ever ingested, an undifferentiated total would already be in the table and could not be split back apart.

Base migrations are edited rather than undone by a later one, because nothing is deployed. docs/import-sources.md keeps the survey as it was taken — it is the evidence for which sources are worth reading at all, and says what each would give if this is ever revisited.

Also, on the parked branch

ContactedMaintainer for MudStats defaulted to true with a comment stating the maintainer had been approached. Nobody had emailed them; a 143-page crawl went out on the strength of it. Default is false again and the claim moves to a caller who can make it (--contacted MudStats). CLAUDE.md gains it as a "Never".

Five suites, 633 tests, zero warnings.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NrGKmKcRCGktyhRTFbQDMk

The backfill exists to prime one deployment once. Carrying its machinery on main
means four HTML parsers for third-party sites we intend never to fetch again,
compiled and tested in CI for ever -- and a parser that never runs but still
compiles is worse than no parser, because it rots silently and reads as a
supported feature. src/MUI.Import, its tests and tools/live-tintin-import move
to the local import/one-time branch, which also carries the four sources and
three fixes the sources agent added; running the import means checking it out.

The rule was mine and it was too narrow. Yesterday's §7.6 said "the importer is
code and is committed; the imported data is not", written from an instruction
that the initial data is a one-time task and should not live in this GitHub. The
data half was right and the code half was an inference.

What stays is everything the imported rows depend on, because rows outlive
tools:

  - migrations/0100_import_provenance.sql. Live rows point at it. A game whose
    GENRE came from MudStats says so on its page for as long as that value
    stands, and dropping the table turns a provenance chip into an unattributed
    fact. A schema is a statement about data that exists, not about which code
    is checked out.
  - FieldSource.ImportedMeasured/Asserted and IntervalOrigin.ImportedMeasured,
    in Catalog already -- §7.5's half-weight grace reads the tier off stored
    rows and never off the importer.
  - docs/import-sources.md, which is the attribution obligation and the record
    of which sources were ingested, which were refused and why. It outlives the
    code that acted on it, including the account of the MudStats crawl that went
    out before anyone had emailed them.

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

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The change removes the backfill importer from the main solution, hardens crawler text and banner processing, adds PostgreSQL-backed catalogue selection, and updates measurement-state terminology, internal-field filtering, documentation, and tests.

Changes

Catalogue integrity and runtime changes

Layer / File(s) Summary
Retire importer execution and preserve provenance
.github/workflows/ci.yml, .gitignore, src/MUI.Import/*, tests/MUI.Import.Tests/*, docs/import-sources.md, migrations/0100_import_provenance.sql
The importer projects, execution pipeline, source implementations, and import tests are removed. Import artifacts are ignored. Source classifications, operating decisions, attribution documentation, and the provenance migration remain.
Normalize crawler input and delayed banners
src/MUI.Crawl/*, src/MUI.Discovery/*, tests/MUI.Crawl.Tests/*, tests/MUI.Crawler.Tests/*
Crawler input removes NUL bytes, banner text uses shared normalization, and probes wait for short non-prompt banners before fingerprinting. Tests cover delayed banners, immediate banners, wire-text cleanup, and referral discovery.
Represent measurement states accurately
src/MUI.Catalog/*, src/MUI.Web/Components/*, tests/MUI.Web.Tests/*, README.md, docs/specs/*, docs/screenshots/*
Empty activity cells now mean no measurement. Reachability summaries use observed periods. Internal fields are excluded from game declarations. UI text and tests use the updated three-state terminology.
Select PostgreSQL or fixture catalogue data
src/MUI.Web/Data/PostgresData.cs, src/MUI.Web/Program.cs, src/MUI.Web/Components/Layout/MainLayout.razor, src/MUI.Web/wwwroot/app.css
The application selects PostgreSQL when configured and otherwise uses fixtures. Startup applies migrations and logs the active mode. Fixture-backed pages display a demo banner.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Program
  participant PostgresData
  participant PostgreSQL
  participant MainLayout
  Program->>PostgresData: ResolveConnectionString()
  alt PostgreSQL configured
    Program->>PostgresData: AddPostgresCatalogue(connectionString)
    Program->>PostgresData: ApplyMigrationsAsync()
    PostgresData->>PostgreSQL: Apply pending migrations
    MainLayout->>MainLayout: Display measured catalogue
  else PostgreSQL not configured
    MainLayout->>MainLayout: Display fixture catalogue and demo banner
  end
Loading

Possibly related PRs

  • SharpMUSH/MUIndex#9: Contains related catalogue wiring, crawler hardening, UI semantics, and internal-field filtering changes.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also changes crawler, referral-discovery, PostgreSQL-serving, and web activity behavior beyond parking the importer. Split unrelated crawler and web changes into separate pull requests, or link them to explicit requirements.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.98% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR moves the one-time importer and its tests and tools out of main while retaining provenance and source documentation required by issue #10.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes moving the one-time importer out of main while retaining seed-related backfill support.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

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

Imported values are gone, and with them the machinery that carried them: the
imported_measured / imported_asserted field sources, IntervalOrigin
.ImportedMeasured, the import_provenance table, ArchivePolicy's half-weight
credit for a third party's reachable history, and the two-tier §7.6 that
justified all of it. The backfill contributes a host and a port. Every fact on
this site is then measured by this crawler.

Three reasons, and the first is the strongest.

A game's origin is not one fact. The catalogue will be cross-checked against
several directories over time, and any game worth listing appears in more than
one of them -- so "imported from MudStats" names whichever fetch happened to run
first, not the game, and a provenance chip saying it would present an accident as
a fact. There is no honest single-origin field to store.

That a game exists is public information. The address of a public MU* is
published by its operator to be dialled; recording where we read it adds nothing
a reader can use, and it is the part of somebody else's work with the least claim
to be ours to republish.

And the point is to start with a lot of games and then gather our own data. A
history import would fill the heatmaps and reachable strips of exactly the games
some other directory was already watching, in a way no reader could tell from our
own measurement without reading the fine print.

What it costs is stated in §7.5 and §7.6 rather than glossed: every game starts
at the archive floor on the day we find it, every heatmap starts empty, and the
day-one site knows each game's address and one probe's worth of everything else.
That is the intended shape.

IntervalOrigin survives as a one-member enum and availability_interval.origin as
a column. If another party's measurements are ever ingested, an undifferentiated
total would already be in the table and could not be split back apart -- a column
is cheap and the distinction is not recoverable after the fact.

The base migrations are edited rather than undone by a later one, because nothing
is deployed yet. docs/import-sources.md keeps the survey as it was taken: it is
the evidence for which sources are worth reading at all, and it says what each
would give if this decision is ever revisited.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@HarryCordewener HarryCordewener changed the title Park the one-time importer outside main Park the one-time importer, and make the backfill seed-only Jul 31, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/import-sources.md`:
- Around line 258-260: Update the import command examples in the live-import
documentation to use the parked tools/live-tintin-import project path instead of
tools/live-import, and state that these commands must be run from the branch
containing that project.

In `@docs/screenshots/README.md`:
- Line 51: Update the fenced code block in the README plain-output example to
specify the text language tag, ensuring the opening fence satisfies markdownlint
MD040.
- Around line 61-64: Update the mui-crawl reproduction command in the README to
replace both live seed hosts with operator-supplied <host:port> placeholders,
and remove the literal ellipsis arguments so every --seed value is valid. Leave
the MUI_POSTGRES example unchanged.

In `@src/MUI.Catalog/Views.cs`:
- Around line 73-74: Update the XML documentation remarks near the reachability
description to replace “uptime” with the approved “reachable”/“unreachable”
terminology, preserving the existing meaning and wording otherwise.

In `@src/MUI.Crawl/BannerText.cs`:
- Around line 106-108: Update SkipEscape to handle ESC sequences with
intermediate bytes: when the byte after ESC is in 0x20–0x2F, consume that
intermediate byte and the subsequent final byte before returning. Preserve
existing handling for other ESC forms so Flatten excludes the complete sequence
and BannerFingerprint.Of and TelnetProbe.LooksUnfinished receive correct data.

In `@src/MUI.Web/Data/PostgresData.cs`:
- Line 5: Convert the namespace declarations in src/MUI.Web/Data/PostgresData.cs
at lines 5-5 and tests/MUI.Web.Tests/SilenceIsNotEvidenceTests.cs at lines 5-5
to file-scoped namespaces, using MUI.Web.Data and MUI.Web.Tests respectively,
and preserve four-space C# indentation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8af66c84-69c0-45c5-bcdb-0d7ec2d0b130

📥 Commits

Reviewing files that changed from the base of the PR and between 2caff7f and 309a361.

⛔ Files ignored due to path filters (6)
  • docs/screenshots/01-home-feeds.png is excluded by !**/*.png
  • docs/screenshots/02-games-listing.png is excluded by !**/*.png
  • docs/screenshots/03-game-page.png is excluded by !**/*.png
  • docs/screenshots/04-plain-mode.png is excluded by !**/*.png
  • docs/screenshots/05-archive.png is excluded by !**/*.png
  • docs/screenshots/06-mobile-game.png is excluded by !**/*.png
📒 Files selected for processing (79)
  • .github/workflows/ci.yml
  • .gitignore
  • CLAUDE.md
  • MUIndex.slnx
  • README.md
  • docs/import-sources.md
  • docs/screenshots/README.md
  • docs/specs/2026-07-30-mu-directory-design.md
  • migrations/0100_import_provenance.sql
  • src/MUI.Catalog/Persistence/FieldRegistry.cs
  • src/MUI.Catalog/Persistence/NpgsqlGameQueries.cs
  • src/MUI.Catalog/Views.cs
  • src/MUI.Crawl/BannerText.cs
  • src/MUI.Crawl/MsspReport.cs
  • src/MUI.Crawl/ProbeOptions.cs
  • src/MUI.Crawl/TelnetProbe.cs
  • src/MUI.Crawl/WireText.cs
  • src/MUI.Discovery/BannerFingerprint.cs
  • src/MUI.Discovery/Identity.cs
  • src/MUI.Import/DirectoryFetcher.cs
  • src/MUI.Import/EtiquettePlanner.cs
  • src/MUI.Import/HistorySink.cs
  • src/MUI.Import/IDirectorySource.cs
  • src/MUI.Import/IImportWriter.cs
  • src/MUI.Import/ImportEtiquette.cs
  • src/MUI.Import/ImportIdentity.cs
  • src/MUI.Import/ImportPipeline.cs
  • src/MUI.Import/ImportProvenance.cs
  • src/MUI.Import/ImportReport.cs
  • src/MUI.Import/ImportRunner.cs
  • src/MUI.Import/ImportTier.cs
  • src/MUI.Import/ImportedAvailabilityWriter.cs
  • src/MUI.Import/ImportedGame.cs
  • src/MUI.Import/MUI.Import.csproj
  • src/MUI.Import/MeasuredHistorySink.cs
  • src/MUI.Import/NpgsqlImportProvenanceStore.cs
  • src/MUI.Import/PolitenessGate.cs
  • src/MUI.Import/RobotsPolicy.cs
  • src/MUI.Import/ServiceCollectionExtensions.cs
  • src/MUI.Import/SourceAttribution.cs
  • src/MUI.Import/Sources/HtmlLine.cs
  • src/MUI.Import/Sources/MsspCrawlerTable.cs
  • src/MUI.Import/Sources/MudStatsSource.cs
  • src/MUI.Import/Sources/TinTinMsspCrawlerSource.cs
  • src/MUI.Web/Components/ActivityHeatmap.razor
  • src/MUI.Web/Components/ActivitySummary.cs
  • src/MUI.Web/Components/Layout/MainLayout.razor
  • src/MUI.Web/Components/PlainText.cs
  • src/MUI.Web/Components/ReachSeries.cs
  • src/MUI.Web/Data/PostgresData.cs
  • src/MUI.Web/Program.cs
  • src/MUI.Web/wwwroot/app.css
  • tests/MUI.Crawl.Tests/ProbeSessionTests.cs
  • tests/MUI.Crawl.Tests/WireTextTests.cs
  • tests/MUI.Crawler.Tests/CrawlCyclePostgresTests.cs
  • tests/MUI.Import.Tests/AttributionTests.cs
  • tests/MUI.Import.Tests/EtiquetteTests.cs
  • tests/MUI.Import.Tests/Fixtures/mudstats-index.html
  • tests/MUI.Import.Tests/Fixtures/mudstats-world-4dimensions.html
  • tests/MUI.Import.Tests/Fixtures/tintin-mssp-mudlist.html
  • tests/MUI.Import.Tests/HistoryTierTests.cs
  • tests/MUI.Import.Tests/ImportAgainstPostgresTests.cs
  • tests/MUI.Import.Tests/ImportPipelineTests.cs
  • tests/MUI.Import.Tests/ImportTierTests.cs
  • tests/MUI.Import.Tests/MUI.Import.Tests.csproj
  • tests/MUI.Import.Tests/RobotsAndRateLimitTests.cs
  • tests/MUI.Import.Tests/Sources/MudStatsSourceTests.cs
  • tests/MUI.Import.Tests/Sources/TinTinMsspCrawlerSourceTests.cs
  • tests/MUI.Import.Tests/Support/FakeHttp.cs
  • tests/MUI.Import.Tests/Support/Fixture.cs
  • tests/MUI.Import.Tests/Support/Harness.cs
  • tests/MUI.Import.Tests/Support/InMemoryStores.cs
  • tests/MUI.Import.Tests/Support/ManualTimeProvider.cs
  • tests/MUI.Import.Tests/Support/PostgresFixture.cs
  • tests/MUI.Web.Tests/PlainParityTests.cs
  • tests/MUI.Web.Tests/SilenceIsNotEvidenceTests.cs
  • tests/MUI.Web.Tests/ThreeStatesTests.cs
  • tools/live-tintin-import/Program.cs
  • tools/live-tintin-import/live-tintin-import.csproj
💤 Files with no reviewable changes (48)
  • tests/MUI.Import.Tests/Fixtures/mudstats-index.html
  • src/MUI.Import/MUI.Import.csproj
  • src/MUI.Import/DirectoryFetcher.cs
  • tools/live-tintin-import/live-tintin-import.csproj
  • src/MUI.Import/ImportProvenance.cs
  • tests/MUI.Import.Tests/Support/Harness.cs
  • tests/MUI.Import.Tests/EtiquetteTests.cs
  • tests/MUI.Import.Tests/Support/InMemoryStores.cs
  • src/MUI.Import/IDirectorySource.cs
  • src/MUI.Import/ImportedAvailabilityWriter.cs
  • src/MUI.Import/ServiceCollectionExtensions.cs
  • tests/MUI.Import.Tests/HistoryTierTests.cs
  • tests/MUI.Import.Tests/RobotsAndRateLimitTests.cs
  • src/MUI.Import/ImportTier.cs
  • src/MUI.Import/Sources/MudStatsSource.cs
  • src/MUI.Import/HistorySink.cs
  • src/MUI.Import/ImportedGame.cs
  • src/MUI.Import/Sources/TinTinMsspCrawlerSource.cs
  • src/MUI.Import/EtiquettePlanner.cs
  • tests/MUI.Import.Tests/ImportTierTests.cs
  • src/MUI.Import/Sources/HtmlLine.cs
  • src/MUI.Import/RobotsPolicy.cs
  • tests/MUI.Import.Tests/ImportPipelineTests.cs
  • src/MUI.Import/ImportPipeline.cs
  • tests/MUI.Import.Tests/Sources/MudStatsSourceTests.cs
  • tests/MUI.Import.Tests/Support/ManualTimeProvider.cs
  • tools/live-tintin-import/Program.cs
  • tests/MUI.Import.Tests/Support/PostgresFixture.cs
  • MUIndex.slnx
  • tests/MUI.Import.Tests/Support/FakeHttp.cs
  • src/MUI.Import/ImportIdentity.cs
  • tests/MUI.Import.Tests/Fixtures/mudstats-world-4dimensions.html
  • tests/MUI.Import.Tests/Support/Fixture.cs
  • src/MUI.Import/ImportEtiquette.cs
  • src/MUI.Import/IImportWriter.cs
  • src/MUI.Import/MeasuredHistorySink.cs
  • src/MUI.Import/ImportRunner.cs
  • tests/MUI.Import.Tests/Fixtures/tintin-mssp-mudlist.html
  • .github/workflows/ci.yml
  • tests/MUI.Import.Tests/ImportAgainstPostgresTests.cs
  • tests/MUI.Import.Tests/Sources/TinTinMsspCrawlerSourceTests.cs
  • tests/MUI.Import.Tests/AttributionTests.cs
  • src/MUI.Import/Sources/MsspCrawlerTable.cs
  • src/MUI.Import/PolitenessGate.cs
  • src/MUI.Import/NpgsqlImportProvenanceStore.cs
  • src/MUI.Import/SourceAttribution.cs
  • src/MUI.Import/ImportReport.cs
  • tests/MUI.Import.Tests/MUI.Import.Tests.csproj

Comment thread docs/import-sources.md
Comment on lines +258 to +260
```bash
dotnet run --project tools/live-import -- --list
dotnet run --project tools/live-import -- --source MudStats --cache /var/tmp/mudstats --dsn "Host=…"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix the project path in the import commands.

These commands invoke tools/live-import, but the PR parks the one-time importer as tools/live-tintin-import and removes importer execution from main. Following these commands from the main checkout will fail because the project is absent. State the branch requirement and use the parked project path.

Proposed update
+Run these commands from the local `import/one-time` branch:
-dotnet run --project tools/live-import -- --list
-dotnet run --project tools/live-import -- --source MudStats --cache /var/tmp/mudstats --dsn "Host=…"
+dotnet run --project tools/live-tintin-import -- --list
+dotnet run --project tools/live-tintin-import -- --source MudStats --cache /var/tmp/mudstats --dsn "Host=…"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```bash
dotnet run --project tools/live-import -- --list
dotnet run --project tools/live-import -- --source MudStats --cache /var/tmp/mudstats --dsn "Host=…"
Run these commands from the local `import/one-time` branch:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/import-sources.md` around lines 258 - 260, Update the import command
examples in the live-import documentation to use the parked
tools/live-tintin-import project path instead of tools/live-import, and state
that these commands must be run from the branch containing that project.

was decoration — and the plain surface carried the same wrong sentence, in the same words, which is
what makes it a real parity check rather than a second implementation.

```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language tag to the fenced block.

Line 51 starts a fenced block without a language. Use text for the plain-output example so markdownlint MD040 passes.

🧰 Tools
🪛 markdownlint-cli2 (0.23.1)

[warning] 51-51: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/screenshots/README.md` at line 51, Update the fenced code block in the
README plain-output example to specify the text language tag, ensuring the
opening fence satisfies markdownlint MD040.

Source: Linters/SAST tools

Comment on lines +61 to +64
```bash
mui-crawl --connection "…" --seed mush.pennmush.org:4201 --seed mud.kharkov.org:3000 …
MUI_POSTGRES="…" dotnet run --project src/MUI.Web
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not commit live seed hosts in the reproduction command.

Line 62 publishes mush.pennmush.org:4201 and mud.kharkov.org:3000 as crawl targets. Use operator-supplied <host:port> placeholders instead. Remove the literal arguments because they are not valid seed values.

Proposed documentation change
-mui-crawl --connection "…" --seed mush.pennmush.org:4201 --seed mud.kharkov.org:3000 …
-MUI_POSTGRES="…" dotnet run --project src/MUI.Web
+mui-crawl --connection "<postgres-connection>" --seed "<host:port>" --seed "<host:port>"
+MUI_POSTGRES="<postgres-connection>" dotnet run --project src/MUI.Web

Based on learnings: do not commit real-host seed lists; keep live crawl inputs local.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```bash
mui-crawl --connection "…" --seed mush.pennmush.org:4201 --seed mud.kharkov.org:3000 …
MUI_POSTGRES="…" dotnet run --project src/MUI.Web
```
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/screenshots/README.md` around lines 61 - 64, Update the mui-crawl
reproduction command in the README to replace both live seed hosts with
operator-supplied <host:port> placeholders, and remove the literal ellipsis
arguments so every --seed value is valid. Leave the MUI_POSTGRES example
unchanged.

Source: Learnings

Comment thread src/MUI.Catalog/Views.cs
Comment on lines +73 to +74
/// which is the one thing this site may never do: a game found an hour ago would have 167 hours
/// of a perfect week's uptime described as downtime. Reachability has its own strip, measured

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use “reachable” instead of “uptime” in the XML documentation.

The new remarks use “uptime” at Line 74. Replace it with “unreachable” wording.

Proposed wording
-/// a game found an hour ago would have 167 hours
-/// of a perfect week's uptime described as downtime. Reachability has its own strip, measured
+/// a game found an hour ago would otherwise be described as unreachable for 167 hours of the
+/// week. Reachability has its own strip, measured

As per coding guidelines: Use “reachable,” never “uptime,” in schema, APIs, code, and user-facing copy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/MUI.Catalog/Views.cs` around lines 73 - 74, Update the XML documentation
remarks near the reachability description to replace “uptime” with the approved
“reachable”/“unreachable” terminology, preserving the existing meaning and
wording otherwise.

Source: Coding guidelines

Comment on lines +106 to +108
// Anything else two-byte: ESC 7, ESC =, ESC ( B and friends.
return i;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
file=$(fd -t f 'BannerText\.cs$' . | head -n 1)
printf '%s\n' "FILE=$file"
cat -n "$file" | sed -n '1,140p'
printf '\n-- usages --\n'
rg -n -C 3 'BannerText\.(Flatten|SkipEscape)|BannerFingerprint|LooksUnfinished|SlightBannerLength' --glob '*.{cs,md}'
printf '\n-- related tests --\n'
rg -n -C 3 'Flatten|Escape|ANSI|CSI|OSC|BannerFingerprint' --glob '*Tests*.cs' --glob '*.cs'

Repository: SharpMUSH/MUIndex

Length of output: 4111


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '-- C# files --'
git ls-files '*.cs' | sed -n '1,240p'
printf '\n-- symbols and references --\n'
rg -n -C 4 --glob '*.cs' 'Flatten|SkipEscape|BannerFingerprint|LooksUnfinished|SlightBannerLength'
printf '\n-- project/docs references --\n'
rg -n -C 3 'BannerFingerprint|SlightBannerLength|BannerPatience|§7\.3|Flatten|charset|ISO-2022|VT100' --glob '*.cs' --glob '*.md' --glob '*.json' --glob '*.yml' --glob '*.yaml'
printf '\n-- test project files --\n'
git ls-files | rg '(^|/)(test|tests|.*Test.*)/|Tests?\.csproj$|\.Tests?\.cs$'

Repository: SharpMUSH/MUIndex

Length of output: 8060


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
def skip_escape(banner, start):
    i = start + 1
    if i >= len(banner):
        return start
    if banner[i] == '[':
        i += 1
        while i < len(banner):
            if '@' <= banner[i] <= '~':
                return i
            i += 1
        return len(banner) - 1
    if banner[i] == ']':
        i += 1
        while i < len(banner):
            if banner[i] == '\a':
                return i
            if banner[i] == '\x1b' and i + 1 < len(banner) and banner[i + 1] == '\\':
                return i + 1
            i += 1
        return len(banner) - 1
    return i

def flatten(banner):
    out = []
    pending_space = False
    i = 0
    while i < len(banner):
        ch = banner[i]
        if ch == '\x1b':
            i = skip_escape(banner, i)
        elif ch.isspace():
            pending_space = bool(out)
        elif ord(ch) < 32 or ord(ch) == 127:
            pass
        else:
            if pending_space:
                out.append(' ')
                pending_space = False
            out.append(ch)
        i += 1
    return ''.join(out)

cases = {
    'designate ASCII': '\x1b(BMUD',
    'designate DEC': '\x1b)0MUD',
    'two-byte 7': '\x1b7MUD',
    'two-byte =': '\x1b=MUD',
    'truncated intermediate': '\x1b(',
    'truncated ESC': '\x1b',
    'CSI SGR': '\x1b[31mMUD',
    'OSC BEL': '\x1b]0;title\aMUD',
    'OSC ST': '\x1b]0;title\x1b\\MUD',
}
for name, value in cases.items():
    print(f'{name}: {flatten(value)!r}')
PY

Repository: SharpMUSH/MUIndex

Length of output: 326


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '-- BannerFingerprint.cs --'
cat -n src/MUI.Discovery/BannerFingerprint.cs
printf '\n-- TelnetProbe outline and relevant sections --\n'
ast-grep outline src/MUI.Crawl/TelnetProbe.cs --match 'class TelnetProbe' --view expanded
rg -n -C 8 'LooksUnfinished|SlightBannerLength|BannerPatience|BannerText' src/MUI.Crawl/TelnetProbe.cs src/MUI.Crawl/ProbeOptions.cs
printf '\n-- BannerFingerprint tests --\n'
cat -n tests/MUI.Discovery.Tests/BannerFingerprintTests.cs
printf '\n-- crawl project references --\n'
rg -n -C 4 'BannerText|Flatten|LooksUnfinished|SlightBannerLength' src/MUI.Crawl src/MUI.Discovery tests/MUI.Crawl.Tests tests/MUI.Discovery.Tests

Repository: SharpMUSH/MUIndex

Length of output: 25250


Fix SkipEscape to consume ESC sequences with intermediate bytes.

When the byte after ESC is in 0x200x2F, consume it and the following final byte. Otherwise, ESC ( B leaves B in Flatten output. This corrupts BannerFingerprint.Of and can change TelnetProbe.LooksUnfinished length.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/MUI.Crawl/BannerText.cs` around lines 106 - 108, Update SkipEscape to
handle ESC sequences with intermediate bytes: when the byte after ESC is in
0x20–0x2F, consume that intermediate byte and the subsequent final byte before
returning. Preserve existing handling for other ESC forms so Flatten excludes
the complete sequence and BannerFingerprint.Of and TelnetProbe.LooksUnfinished
receive correct data.

using MUI.Catalog.Persistence;
using Npgsql;

namespace MUI.Web.Data;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use file-scoped namespaces in both new C# files.

  • src/MUI.Web/Data/PostgresData.cs#L5-L5: Change to namespace MUI.Web.Data;.
  • tests/MUI.Web.Tests/SilenceIsNotEvidenceTests.cs#L5-L5: Change to namespace MUI.Web.Tests;.

As per coding guidelines, “Use file-scoped namespaces and four-space indentation in C#.”

📍 Affects 2 files
  • src/MUI.Web/Data/PostgresData.cs#L5-L5 (this comment)
  • tests/MUI.Web.Tests/SilenceIsNotEvidenceTests.cs#L5-L5
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/MUI.Web/Data/PostgresData.cs` at line 5, Convert the namespace
declarations in src/MUI.Web/Data/PostgresData.cs at lines 5-5 and
tests/MUI.Web.Tests/SilenceIsNotEvidenceTests.cs at lines 5-5 to file-scoped
namespaces, using MUI.Web.Data and MUI.Web.Tests respectively, and preserve
four-space C# indentation.

Source: Coding guidelines

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