Skip to content

feat(engine): world-building/OLC command surface (dig/tunnel/describe)#20

Merged
ncipollina merged 2 commits into
mainfrom
feat/world-building-olc-commands
Jul 24, 2026
Merged

feat(engine): world-building/OLC command surface (dig/tunnel/describe)#20
ncipollina merged 2 commits into
mainfrom
feat/world-building-olc-commands

Conversation

@ncipollina

Copy link
Copy Markdown
Contributor

📋 Summary

Closes Slice 4 of the WheelMUD reconciliation roadmap (ADR-0009): adds dig/tunnel/describe world-building commands, gated at SecurityRole.MinorBuilder via the same RegisterWithRole mechanism ADR-0005 introduced for moderation commands. Also documents the new commands (and backfills the previously-undocumented Slice 3 moderation commands) in the public docsite, and records a world-loading scale concern surfaced during design discussion as tracked future work rather than fixing it speculatively.


📝 Changes

New commands (src/SharpMud.Engine/Commands/Builtin/Builder/)

  • DigCommand — creates a new room and wires a two-way exit from the builder's current room to it.
  • TunnelCommand — wires a two-way exit between the current room and an already-existing room, found by exact name.
  • DescribeCommand — sets the current room's description.
  • BuilderCommandHelpers — direction parsing, room-by-name lookup, exit-collision check, tree-root lookup for saving.
  • BuilderCommands.RegisterAll — registers all three via RegisterWithRole(_, SecurityRole.MinorBuilder). Wired into samples/SharpMud.Samples.Classic/Program.cs alongside the existing AdminCommands.RegisterAll.

Shared exit-wiring dedup

  • New SharpMud.Engine.Behaviors.RoomConnector.Connect — the two-way exit-wiring logic, extracted from BasicWorldBuilder and HubWorldBuilder, which each had their own identical private copy. Both now call the shared helper; no behavior change (confirmed by the pre-existing BasicWorldBuilderTests still passing unchanged).

Bug fix found during manual verification
dig/tunnel didn't check whether the current room already had an exit in the requested direction. Since MoveCommand resolves exits via FirstOrDefault, digging into an already-occupied direction silently created an unreachable, shadowed duplicate exit instead of rejecting. Fixed via BuilderCommandHelpers.HasExit, checked by DigCommand (origin) and TunnelCommand (origin and destination's opposite direction). Regression tests added for all three collision cases.

Docs

  • docs/adr/0009-...md / docs/plans/0009-...md (new), indexed in both READMEs and PLAN-0001.
  • docs/commands.md, docs/world-model.md, SPEC.md updated to describe the new commands as current state.
  • SPEC.md / docs/persistence.md / PLAN-0001 — added an unnumbered "Slice 11" tracking a world-loading scale concern (ThingRepository loads the entire world into memory on every load) surfaced while discussing this PR's room-lookup approach — deliberately left undesigned, not solved here.
  • New docsite page docsite/docs/moderation-and-world-building.md (public, consumer-facing) covering SecurityRole/RegisterWithRole and both command sets that use it; cross-linked from customizing.md.

🧪 Validation

  • Build/test status: dotnet build clean, dotnet test — 235/235 passing (includes new tests for RoomConnector, DigCommand, TunnelCommand, DescribeCommand, BuilderCommands registration).
  • Manual verification performed: real Telnet session against SharpMud.Samples.Classic with a scratch SQLite DB and SHARPMUD_INITIAL_ADMIN bootstrap — created a character, granted MinorBuilder, exercised dig/tunnel/describe happy paths and all rejection paths (invalid direction, missing args, no/ambiguous room match, self-tunnel, occupied-direction on both origin and destination sides). Restarted the server and confirmed persistence via direct SQLite inspection (Things/Behaviors tables) — every new room, description, and exit (correct bidirectional ExitDestinationId, no duplicates) survived alongside the pre-existing hub content.
  • Edge cases checked: direction collision (the bug above, both on dig's origin and tunnel's origin/destination), ambiguous/missing room names, self-tunnel, multi-area save correctness (TunnelCommand saves both the origin's and destination's tree roots when they differ).
  • Also verified: docsite builds clean (zensical build → "No issues found") with the new page in nav.

💬 Notes for Reviewers

  • Not in scope, deliberately deferred (see ADR-0009's Negative Consequences): room/exit deletion, NPC/item spawning + mob-respawn loops + loot tables (tracked as unnumbered "Slice 10"), and expanding BasicWorldBuilder's starter world.
  • The world-loading scale concern (unnumbered "Slice 11") is documentation/tracking only in this PR — no code changed for it. Flagging so it doesn't get mistaken for an implicit ask to fix it here.

Closes Slice 4 of the WheelMUD reconciliation roadmap (ADR-0009): adds
dig/tunnel/describe, gated at SecurityRole.MinorBuilder via the same
RegisterWithRole mechanism ADR-0005 introduced for moderation commands.

Extracts a shared RoomConnector.Connect helper (Engine) from the two
world-boot builders (BasicWorldBuilder, HubWorldBuilder), which each had
their own identical copy of the two-way exit-wiring logic - one shared
implementation instead of three.

Fixes a real bug found during manual Telnet verification: dig/tunnel
didn't check whether the current room already had an exit in the
requested direction, so digging into an already-occupied direction
silently created an unreachable, shadowed duplicate exit rather than
rejecting. Added BuilderCommandHelpers.HasExit plus regression tests.

Documents the new commands - and backfills the previously-undocumented
Slice 3 moderation commands - in the public docsite, and records a
world-loading scale concern surfaced during design discussion as tracked
future work (PLAN-0001 Slice 11) rather than solving it speculatively.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@github-actions github-actions Bot added the type: feat New feature label Jul 23, 2026
Comment thread src/SharpMud.Engine/Commands/Builtin/Builder/DigCommand.cs Outdated
Comment thread src/SharpMud.Engine/Commands/Builtin/Builder/TunnelCommand.cs
Comment thread docsite/docs/moderation-and-world-building.md Outdated
Comment thread src/SharpMud.Engine/Commands/Builtin/Builder/TunnelCommand.cs

@ncipollina ncipollina left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

💬 Deep-dive review complete — build clean, 235/235 tests pass locally (matches PR's own claim), and ADR-0009/PLAN-0009/docs/persistence.md's factual claims all verified against the actual code. 4 findings posted inline, none blocking (🛑 not warranted): two ⚠️ correctness gaps around unchecked Thing.Add return + non-atomic dual-tree save in the multi-area tunnel case, one ⚠️ factual error in the new docsite's SecurityRole "Implies" table, and one 📝 message-string duplication. Nothing here needs to hold up the merge.

Checks area.Add(room)'s bool return in DigCommand instead of assuming
success - Thing.Add publishes a cancelable AddChildEvent, and proceeding
regardless would register/connect/save a room that was never actually
attached to the tree if anything ever vetoed it. Matches MoveCommand's
existing precedent of checking Remove's return the same way.

Dedups the "already an exit ... from here" rejection message (was
identical, hand-copied in both DigCommand and TunnelCommand) into a
shared BuilderCommandHelpers.OccupiedDirectionMessage.

Fixes a factual error in the new docsite page's SecurityRole "Implies"
table: MinorBuilder/FullBuilder's accumulation was documented as "-"/
"MinorBuilder" when SecurityRoleExtensions.ImpliedRoles actually gives
"Player"/"MinorBuilder, Player" - would have told a reader granting
MinorBuilder doesn't also grant Player, which is false.

Defers (tracked in persistence.md Open Items + PLAN-0009, not fixed
here) TunnelCommand's two SaveTreeAsync calls not being atomic together
across a multi-area tunnel - currently unreachable since every existing
world has exactly one area, and fixing it properly needs a repository-
level multi-root atomic save, a bigger change than this PR's scope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ncipollina

Copy link
Copy Markdown
Contributor Author

Re: #20 (review) — all 4 inline findings triaged and replied to individually. 3 fixed in a449627 (DigCommand's unchecked Add return, the duplicated occupied-direction message, the docsite Implies table error); the TunnelCommand dual-save atomicity gap is deferred with a tracked note in docs/persistence.md and PLAN-0009 rather than fixed here (repository-API change, currently unreachable). All 4 review threads resolved.

@ncipollina
ncipollina merged commit 82ec955 into main Jul 24, 2026
7 checks passed
@ncipollina
ncipollina deleted the feat/world-building-olc-commands branch July 24, 2026 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feat New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant