Skip to content

refactor(egemhoarder): v2.0.0 address persistent bugs and clean up code#2317

Open
mrjeckel wants to merge 25 commits into
elanthia-online:masterfrom
mrjeckel:refactor_egemhoarder
Open

refactor(egemhoarder): v2.0.0 address persistent bugs and clean up code#2317
mrjeckel wants to merge 25 commits into
elanthia-online:masterfrom
mrjeckel:refactor_egemhoarder

Conversation

@mrjeckel
Copy link
Copy Markdown

@mrjeckel mrjeckel commented May 10, 2026

  v2.0.0 (2026-05-11)
    - complete rewrite from egemhoarder v1.x
    - auto-navigate to locker, stow hands, return to origin on deposit/raid
    - multi-jar tracking; raid spans jars; list groups by gem
    - chest=empty jars, bin=partial, wardrobe=full
    - lockermode strict/normal/loose for multi-locker gem assignment
    - search and raid support multi-word gem names
    - scan is atomic -- rolls back on partial failure
    - sort integrated into newlocker scan

Summary by CodeRabbit

  • New Features

    • Unified gem-hoarder commands: help, list, search, newlocker, forget, set, raid, deposit; auto-scans/registers unknown lockers.
    • Per-character persistent gem data in YAML with reliable save/load and save-on-exit.
    • Deposit fills partial jars first, then starts new jars; reports deposited totals and leftover/reserved gems.
    • Raid prefers full/larger jars, updates jars and returns emptied jars to chest.
  • Improvements

    • Better name normalization, exclusion filtering, and clearer list/search output.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors egemhoarder into GemHoarder: adds YAML persistence at DATA_DIR/.../gemhoarder.yaml, normalizes gem/jar matching, provides inventory/locker helpers, and rewrites deposit and raid workflows with a centralized command dispatcher.

Changes

GemHoarder Module Refactor

Layer / File(s) Summary
Script Header & Version
scripts/egemhoarder.lic
Updates script header and version notes to v2.0.0 with refactor/change notes.
Module & Constants
scripts/egemhoarder.lic
Defines GemHoarder module and module-level constants for persistence path and jar/name parsing patterns.
Data Persistence
scripts/egemhoarder.lic
YAML-backed persistence with YAML_FILE, load_data, default_data, and save_data; load fallback and directory creation on save.
Gem Matching & Normalization
scripts/egemhoarder.lic
New clean_name and gem_regex utilities for plural-aware full-string gem/jar matching.
Inventory & Configuration Helpers
scripts/egemhoarder.lic
Adds find_gemsack, gem_contents, excluded_gems, gem_excluded?, locker_mode, and current_location.
Hand Stow/Restore
scripts/egemhoarder.lic
Adds stow_hands and restore_hands to preserve carried items during locker operations.
Locker Navigation
scripts/egemhoarder.lic
Adds at_locker?, go_to_locker with retry logic, and go_to_origin.
Locker Part Operations
scripts/egemhoarder.lic
open_locker_part and close_locker to open/close specific locker parts and return container ids/contents.
Help Text
scripts/egemhoarder.lic
Replaces usage output with show_help documenting commands and UserVars.
Listing & Search
scripts/egemhoarder.lic
show_list groups persisted jar records by gem+location, sums counts, determines “full” semantics, and supports optional filters.
Command: set (Persisted Updates)
scripts/egemhoarder.lic
cmd_set updates persisted jar records for a gem/location (supports deletion via count 0) and saves data.
Scanner: scan_locker
scripts/egemhoarder.lic
scan_locker catalogs bin/wardrobe/chest jars, reads counts/fullness, moves misplaced jars, and replaces records for the scanned location on full success.
Core Workflow: deposit_gems
scripts/egemhoarder.lic
Two-phase deposit: fill partial jars from bin, then create new jars from chest; updates records and emits deposit/leftover reports distinguishing other-locker reservations.
Core Workflow: raid
scripts/egemhoarder.lic
raid validates availability, selects jars (full-first), shakes gems into gemsack, decrements counts, routes emptied jars to chest, deletes emptied records, and persists changes.
Entry & Command Dispatch
scripts/egemhoarder.lic
Adds GemHoarder.main command dispatch, auto-scan/register for unknown lockers, registers before_dying { save_data(data) }, and updates runtime entry to GemHoarder.main.

Sequence Diagram(s)

sequenceDiagram
  participant Player
  participant GemHoarder
  participant Bin
  participant Chest
  participant Data
  Player->>GemHoarder: deposit or raid command
  GemHoarder->>Bin: open/read partial jars (fill phase)
  GemHoarder->>Chest: open/read empty jars (start new jars / return empties)
  GemHoarder->>Data: load/update persisted jar records (save_data)
  GemHoarder->>Player: report deposited/raided/leftover
Loading

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Title check ✅ Passed The title accurately reflects the primary objective: a major refactor of the egemhoarder script to address persistent bugs and improve code quality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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 `@scripts/egemhoarder.lic`:
- Around line 302-306: The loop that calls open_locker_part for each slot (the
[:wardrobe, :bin, :chest] iteration) currently logs failures and continues,
which makes a partial scan appear successful; change the behavior so that if
open_locker_part returns false the loop immediately returns false (or sets an
overall failure flag and returns false after the loop) instead of using next,
and ensure the caller newlocker does not clear existing location data before
invoking this scan (or only clears after the scan succeeds); apply the same fix
to the similar block referenced around lines 359-361 so any compartment failure
causes the scan to fail rather than overwriting known-good data.
- Around line 335-343: The current logic in the block that searches data[:jars]
(using existing, gem_name, location, count, is_full) merges multiple physical
jars into one aggregated record; instead, stop collapsing matches — when adding
jars, do not update an existing entry's :count/:full; instead append one record
per physical jar (e.g., push count times a new hash with gem: gem_name, count:
1, full: is_full, location: location) so each physical jar is represented
individually and the downstream raid code won't lose inventory.
- Around line 674-687: The fput calls that use "put my ... in my #{sack.noun}"
are ambiguous when multiple containers share the same noun; update the two fput
invocations (the one after finding `dropped` and the one after finding `held`)
to target the resolved container by using `sack.id` instead of `sack.noun` so
the command references the specific object id; locate the fput calls in the
block handling the jar raid (the branch that decrements `jar_record[:count]` and
the `/free hand/` branch) and modify the command string to use the sack's id to
ensure deterministic stowing of raided gems.
- Around line 239-244: The user-supplied filter is interpolated directly into
/#{f}/ which lets regex metacharacters break or broaden matching; fix by
escaping the input with Regexp.escape before using it in a pattern: compute f =
Regexp.escape(filter.strip.downcase) (then apply your plural/ending
substitutions against that escaped string as needed, e.g. replace escaped
"ies\z"/"y\z"/"s\z" with the intended '(?:y|ies)'/'(?:y|ies)'/'s?' tokens), and
then build the regexp with Regexp.new(f) (or use /#{f}/ after escaping) in the
display.select! block that references f and display.select!.
🪄 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: CHILL

Plan: Pro

Run ID: a5226b9c-d457-411d-bda9-415d5ec3cbce

📥 Commits

Reviewing files that changed from the base of the PR and between 74ab820 and 7533578.

📒 Files selected for processing (1)
  • scripts/egemhoarder.lic

Comment thread scripts/egemhoarder.lic Outdated
Comment thread scripts/egemhoarder.lic
Comment thread scripts/egemhoarder.lic Outdated
Comment thread scripts/egemhoarder.lic Outdated
@mrjeckel mrjeckel changed the title Refactor egemhoarder Refactor egemhoarder to address persistent bugs and (hopefully) clean it up a bit May 10, 2026
@mrjeckel
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 10, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
scripts/egemhoarder.lic (3)

475-476: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Same-gem jars still aren't matched back to the correct persisted record.

The scan stores per-jar records, but deposit/raid later look records and physical jars up by gem name only. If a locker has two partial ruby jars, phase 1 can keep incrementing the first persisted record while filling the second physical jar, and raid can keep selecting the first physical jar while decrementing or deleting a different record. That will drift counts even though the scan itself is per-jar.

This needs a real jar-to-record identity when mutating state, or a one-to-one rematch of the opened slot before any counts are updated.

Also applies to: 546-548, 773-777

🤖 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 `@scripts/egemhoarder.lic` around lines 475 - 476, The per-jar scan builds
records with only gem name/count etc. (see new_records << { gem: gem_name,
count: count, full: is_full, location: location }) but later mutation paths
(deposit, raid) lookup by gem name, causing mismatches when a locker has
multiple partial jars; change the scan to emit a stable jar identity (e.g.,
jar_id or slot_id) into each new_records entry and persist that id with the
record, then update deposit and raid to match and mutate by that jar_id (or,
alternatively, when an open slot is selected re-resolve the exact persisted
record for that physical slot before adjusting counts) so mutations always
target the exact jar record rather than the gem name only.

413-420: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

set collapses multi-jar state back into one record.

delete_if removes every same-gem entry for the locker, then the command recreates a single hash with an aggregate count. That breaks the one-record-per-physical-jar invariant the new scan/raid logic relies on, so ;egemhoarder set can corrupt lockers that legitimately have multiple jars for the same gem.

At minimum, this path should refuse to rewrite a gem/location that already has more than one record instead of collapsing it.

🤖 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 `@scripts/egemhoarder.lic` around lines 413 - 420, The current `set` path
collapses multiple same-gem entries at a location because it always deletes all
matches then appends a single aggregated hash; to fix, before calling
data[:jars].delete_if check how many existing entries match (e.g. entries =
data[:jars].select { |j| clean_name(j[:gem]).casecmp?(clean_name(gem_name)) &&
j[:location] == loc }), and if entries.size > 1 and count > 0 then refuse to
rewrite — emit an error/echo and return a nonzero status (or skip the write)
instead of deleting and appending; otherwise continue with the current
delete_if-and-append logic so single-entry updates still work.

341-355: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Escape the final search token before building matcher.

words are escaped, but last is still interpolated as regex syntax. Inputs like search ruby[ can still raise RegexpError, and . / + / * will broaden matches unexpectedly.

Suggested fix
-      last  = case last.downcase
-              when /ies\z/ then last.sub(/ies\z/i, '(?:y|ies)')
-              when /ves\z/ then last.sub(/ves\z/i, '(?:f|ves)')
-              when /es\z/  then last.sub(/es\z/i,  '(?:es)?')
-              when /s\z/   then last.sub(/s\z/i,   's?')
-              when /y\z/   then last.sub(/y\z/i,   '(?:y|ies)')
-              when /z\z/   then last + 'es?'
-              else              last + 's?'
+      escaped_last = Regexp.escape(last)
+      last  = case escaped_last.downcase
+              when /ies\\z/ then escaped_last.sub(/ies\\z/i, '(?:y|ies)')
+              when /ves\\z/ then escaped_last.sub(/ves\\z/i, '(?:f|ves)')
+              when /es\\z/  then escaped_last.sub(/es\\z/i,  '(?:es)?')
+              when /s\\z/   then escaped_last.sub(/s\\z/i,   's?')
+              when /y\\z/   then escaped_last.sub(/y\\z/i,   '(?:y|ies)')
+              when /z\\z/   then escaped_last + 'es?'
+              else               escaped_last + 's?'
               end
🤖 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 `@scripts/egemhoarder.lic` around lines 341 - 355, The code builds a regex
`matcher` from words and a modified final token `last` but fails to escape
`last`, allowing user input to inject regex metacharacters; fix by escaping the
user token and then applying the pluralization alternations against the original
to compute only the suffix replacement: capture the original `last` (e.g.
last_orig = last), compute safe_last = Regexp.escape(last_orig), determine the
suffix and replacement exactly as in the case branches (using last_orig to
detect which branch applies), then produce the final `last` by replacing the
escaped suffix in safe_last with the intended replacement (e.g.
safe_last.sub(Regexp.escape(suffix), replacement) or append replacement when no
suffix), and continue using that `last` when building `pattern` and `matcher`
(so `words` remain escaped and `last` is now safe).
🤖 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 `@scripts/egemhoarder.lic`:
- Around line 803-860: The shake result handling can miss timeouts/unexpected
lines and still demote jars to bin; add a tracking flag (e.g., gem_removed or
empty_confirmed) that you set to true inside the branches where you decrement
jar_record[:count] or set it to 0 (the branches that match /fall into your/,
/free hand/, and /nothing falls out|realize that it is empty/), and do NOT clear
jar_record[:full] or move the jar to bin/chest unless one of those flags is
true; for unexpected or timed-out dothistimeout results, log/echo a warning and
skip changing :full or moving the jar so the jar stays in its original slot for
retry.

---

Duplicate comments:
In `@scripts/egemhoarder.lic`:
- Around line 475-476: The per-jar scan builds records with only gem name/count
etc. (see new_records << { gem: gem_name, count: count, full: is_full, location:
location }) but later mutation paths (deposit, raid) lookup by gem name, causing
mismatches when a locker has multiple partial jars; change the scan to emit a
stable jar identity (e.g., jar_id or slot_id) into each new_records entry and
persist that id with the record, then update deposit and raid to match and
mutate by that jar_id (or, alternatively, when an open slot is selected
re-resolve the exact persisted record for that physical slot before adjusting
counts) so mutations always target the exact jar record rather than the gem name
only.
- Around line 413-420: The current `set` path collapses multiple same-gem
entries at a location because it always deletes all matches then appends a
single aggregated hash; to fix, before calling data[:jars].delete_if check how
many existing entries match (e.g. entries = data[:jars].select { |j|
clean_name(j[:gem]).casecmp?(clean_name(gem_name)) && j[:location] == loc }),
and if entries.size > 1 and count > 0 then refuse to rewrite — emit an
error/echo and return a nonzero status (or skip the write) instead of deleting
and appending; otherwise continue with the current delete_if-and-append logic so
single-entry updates still work.
- Around line 341-355: The code builds a regex `matcher` from words and a
modified final token `last` but fails to escape `last`, allowing user input to
inject regex metacharacters; fix by escaping the user token and then applying
the pluralization alternations against the original to compute only the suffix
replacement: capture the original `last` (e.g. last_orig = last), compute
safe_last = Regexp.escape(last_orig), determine the suffix and replacement
exactly as in the case branches (using last_orig to detect which branch
applies), then produce the final `last` by replacing the escaped suffix in
safe_last with the intended replacement (e.g.
safe_last.sub(Regexp.escape(suffix), replacement) or append replacement when no
suffix), and continue using that `last` when building `pattern` and `matcher`
(so `words` remain escaped and `last` is now safe).
🪄 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: CHILL

Plan: Pro

Run ID: dafd6949-7700-4752-a2dd-ea483f7a9e52

📥 Commits

Reviewing files that changed from the base of the PR and between 06aff3d and 4519fc4.

📒 Files selected for processing (1)
  • scripts/egemhoarder.lic

Comment thread scripts/egemhoarder.lic
@mrhoribu mrhoribu changed the title Refactor egemhoarder to address persistent bugs and (hopefully) clean it up a bit refactor(egemhoarder): v2.0.0 address persistent bugs and clean up code May 11, 2026
mrhoribu added 2 commits May 12, 2026 09:27
renames module back to EGemHoarder and various echo statements to use that as well, ensuring no conflict with original gemhoarder module namescope.
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.

2 participants