Skip to content

feat(healer): vitality healing, spell slot mutex, dead detection#7330

Merged
MahtraDR merged 22 commits into
elanthia-online:mainfrom
MahtraDR:feat/healer-vitality-healing
May 3, 2026
Merged

feat(healer): vitality healing, spell slot mutex, dead detection#7330
MahtraDR merged 22 commits into
elanthia-online:mainfrom
MahtraDR:feat/healer-vitality-healing

Conversation

@MahtraDR
Copy link
Copy Markdown
Collaborator

@MahtraDR MahtraDR commented Mar 17, 2026

Summary

  • Complete rewrite of healer.lic with parallel patient processing and non-blocking spell slot state machines
  • Non-blocking queue via DownstreamHook: line monitor fires for every game line before it reaches the script buffer, so arrival/whisper/gesture triggers are never consumed by blocking bput/fput calls during patient processing
  • Wounds before vitality: bleeding causes vitality loss, so wound transfer always takes priority over VH -- no more early VH claim while patient is still wounded
  • Vitality Healing via VH spell (configurable vh waggle_set), with prep-first approach and reactive VH casting
  • Spell slot mutex: FP, CD, and VH block each other (one at a time), while wound transfers interleave freely for other patients
  • Dead patient detection: blocking heal via basic empathic link (no Unity), transfers all wounds in a loop with self-healing between rounds
  • Emergency fall safety: if own vitality drops below 30%, fall to break all Unity links, enter recovery mode, resume when healthy
  • Parasite/leech tending from perceived parasites (no spell needed, interleaves with wounds)
  • Link Unity validation at startup via silent issue_command -- hard exit if not available
  • VH waggle validation at startup -- prints example YAML config if vh waggle_set is missing
  • Self-vitality floor (60%) safety net prevents taking vit when own health is dangerously low
  • Spell slot timeout (45s) prevents permanent lock from stuck game state
  • Friends-only filtering, moongate/arrival/gesture/lean/offer triggers
  • Passive self-healing via Heal/Regenerate (30s max wait, spell-conflict-safe)

Architecture

DownstreamHook (IO thread)          Main loop (script thread)
  |                                   |
  v                                   v
queue_from_line(line)               drain buffer (gets?)
  |                                   |
  +-- match triggers? -> add_patient  process_queue()
  |                                     |
  +-- pass line through unchanged       +-- emergency check
                                        +-- spell slot processing (FP/CD/VH)
                                        +-- wound/patient processing
                                        +-- heal_self

The hook fires for every game line before it reaches the script buffer. Blocking operations in process_queue (bput, perceive_health_other, wait_for_passive_healing) consume lines from the buffer but can never consume trigger lines because the hook already processed them.

Settings

friends: [Bob, Joe, Mary]
healer_waggle_set: healme
cambrinth: cambrinth ring
waggle_sets:
  vh:
    Vitality Healing:
      mana: 5
      cambrinth:
        - 5
      prep_time: 3

Test plan

  • 65 RSpec examples pass (queue, spell slot, dead detection, triggers, parasites, VH state machine, afflictions, health checks, timeouts)
  • Live test: patients no longer missed when healer is busy with other patients (DownstreamHook fix)
  • Live test: wound healing, disease transfer, vitality healing end-to-end (Emeshest session)
  • Live test: verify dead patient blocking heal cycle
  • Live test: verify emergency fall recovery
  • Live test: verify spell slot timeout recovery

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Persistent patient queue with continuous, interleaved wound and patient processing, plus emergency recovery when healer is critical.
    • Two-path healing scheduler for vitality and affliction with auto-timeouts, manual clears, and per-patient cooldowns.
    • Runtime controls: show status, clear queue, clear spell slot, add patient, and one-shot status/help commands.
    • First-encounter teaching, conditional linking, parasite tending, and dead-patient handling.
  • Tests

    • Comprehensive suite covering queue lifecycle, scheduling, safety behaviors, trigger parsing, and spell-slot semantics.

MahtraDR and others added 15 commits March 17, 2026 14:29
…etection

Complete rewrite of the healer script with parallel patient processing,
non-blocking spell slot state machines for FP/CD/VH, dead patient
detection, parasite tending, and comprehensive test coverage.

New features:
- Vitality Healing via VH spell (non-blocking state machine)
- Spell slot mutex: FP/CD/VH block each other, wounds interleave freely
- Dead patient detection (checks HealthResult.dead, whispers to get cleric)
- Parasite/leech tending from perceive_health parasites
- Link Unity validation at startup (fail-fast)
- VH waggle_set validation with example YAML on missing config
- Touch-based vitality check after wounds clear
- Self-vit floor (60%) safety net for vit transfers
- Spell slot timeout (45s) prevents permanent lock
- Friends-only filtering, moongate/arrival/gesture triggers
- Passive self-healing via Heal/Regenerate (30s max wait)
- Familiar window logging, scholarship teaching

65 RSpec examples covering queue management, spell slot mutex,
dead detection, trigger patterns, parasites, vitality state machine,
affliction processing, health checks, and timeout behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ty fix

- Add DownstreamHook line monitor so arrival/whisper/gesture triggers are
  never consumed by blocking bput/fput calls during patient processing
- Defer teaching to heal_patient via needs_teach flag (hooks can't fput)
- scan_room_for_patients at startup catches pre-existing room occupants
- Remove early VH claim -- wounds always healed before vitality (bleeding
  causes vit loss, so stopping the bleed is higher priority)
- Add emergency fall safety (break all links if vitality < 30%)
- Add dead patient blocking heal (massive wounds via basic empathic link)
- Add spell_conflict? helper, case-insensitive spell slot matching
- Fix patient_ready? returning true for unknown patients

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@elanthia-online elanthia-online deleted a comment from coderabbitai Bot Apr 28, 2026
@elanthia-online elanthia-online deleted a comment from coderabbitai Bot Apr 28, 2026
@elanthia-online elanthia-online deleted a comment from coderabbitai Bot Apr 28, 2026
@MahtraDR
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review this pr in full

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

@MahtraDR Sure! I'll kick off a full review of this PR right away.

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 28, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4430f90e-cc57-4483-a9d9-c1279f5c290b

📥 Commits

Reviewing files that changed from the base of the PR and between 189f7f1 and ed143ee.

📒 Files selected for processing (1)
  • healer.lic

📝 Walkthrough

Walkthrough

Persistent Healer daemon replaces single-run whisper parsing with a main loop that drains script buffer, monitors lines via a downstream hook, maintains a shared patient queue, runs interleaved non-blocking spell-slot state machines (afflictions and vitality), processes wound transfers gated by readiness, handles dead/patient lifecycle and emergency fall/reset, and exposes runtime operator controls.

Changes

Core Healer

Layer / File(s) Summary
Configuration & Constants
healer.lic
New header, constants and trigger/friend matchers, VH/waggle and threshold settings.
Initialization & Validation
healer.lic
Validates Unity/link capabilities and available spells; initializes @patients, spell-slot and queue state, VH waggle load.
Runtime API / Data Shape
healer.lic
Adds attr_reader :patients; patient shape fields (touched, body_part, cooldowns); public methods add_patient, show_status, clear_queue, clear_spell_slot.
Line Monitoring / Enqueueing
healer.lic
Replaces per-line run(line) parsing with a DownstreamHook-based line monitor and queue_from_line to enqueue friends non-blockingly.
Queue Management
healer.lic
Persistent patient queue primitives: add_patient, remove_patient, presence/room checks, duplicate suppression, and readiness/cooldown gating helpers.
Patient Processing
healer.lic
Per-patient flow: first-encounter teaching, presence checks, dead-patient handling (whisper + conditional removal), conditional Unity-link upgrading, parasite tending, wound transfer via redirect to unwounded part, and completion/removal logic.
Spell-Slot Mutex & Queue Semantics
healer.lic
Claim/release semantics: claim_spell_slot, release_spell_slot, auto-pop behavior to next queued patient, skip-on-depart handling, manual clears.
Non-blocking State Machines
healer.lic
process_affliction_slot (transfer → buff cure phases with timeout) and process_vitality_slot (VH prep → vit transfer → cast/recover loops), plus VH prep/validation.
Self-heal & Safety
healer.lic
Self-heal/VH-on-self logic, conflict avoidance with running buff scripts, emergency check check_emergency_fall that triggers fall, clears slot/queue and resets patient touch/body_part into recovery mode.
Main Loop & Orchestration
healer.lic
Replaces run(line) with a persistent run loop that drains the script buffer, installs line hook, and repeatedly calls process_queue to interleave spell-slot and wound processing gated by ready_to_heal_wounds?.
Operator Surface & CLI
healer.lic
show_status, patients, add_patient runtime controls, clear_queue, clear_spell_slot, and one-shot CLI status/help handling.

Test Suite

Layer / File(s) Summary
Spec Harness & Loader
spec/healer_spec.rb
New RSpec file loading healer.lic, stubbing game interfaces and providing deterministic build_healer.
Queue & Lifecycle Tests
spec/healer_spec.rb
Tests for add_patient, duplicate suppression, readiness/cooldown gating, removal/cleanup, and first-encounter teaching.
Spell-Slot & State Machines
spec/healer_spec.rb
Claims, enqueue behavior, release auto-pop and skip-absent, process_affliction_slot and process_vitality_slot transitions, timeouts, safety-floor gating, and VH waggle validation.
Dead-Patient, Unity & Wound Logic
spec/healer_spec.rb
Dead-patient whisper/removal cases, forbidding Unity-link/VH for dead, wound transfer selection, available-location filtering, and transfer failure handling.
Trigger Parsing & Parasite Handling
spec/healer_spec.rb
queue_from_line parsing across friend message formats and parasite tending dispatch tests.
Readiness, Timeouts & Operator Commands
spec/healer_spec.rb
Readiness caching/throttling, slot timeout/release, patient-departure during processing, and explicit clear_queue/clear_spell_slot tests.

Sequence Diagram(s)

sequenceDiagram
    participant Line as Incoming Line / Client
    participant Healer
    participant Queue as Patient Queue
    participant SpellSlot as Spell-Slot SM
    participant Game as Game / Unity / Buff subsystems

    Line->>Healer: downstream hook delivers line
    Healer->>Queue: queue_from_line -> add_patient (friend filter)
    loop main loop
        par Spell processing
            Healer->>SpellSlot: process_affliction_slot / process_vitality_slot
            SpellSlot->>Game: transfer/prepare/cast/buff actions
            Game-->>SpellSlot: cast/drain results
            SpellSlot-->>Healer: acquire/release events
        and Wound processing
            Healer->>Queue: select ready patient (cooldowns, presence)
            Healer->>Game: perform wound transfer / Unity redirect when allowed
            Game-->>Healer: transfer result
            Healer->>Queue: update patient state / remove if complete
        end
        alt emergency vit low
            Healer->>Healer: check_emergency_fall -> fall, clear slot/queue, reset patient state
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. 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 directly addresses three major features implemented in the changeset: vitality healing, spell slot mutex, and dead detection.
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.

✏️ 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 your free trial and get 200 agent minutes per Slack user (a $50 value).


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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

Copy link
Copy Markdown

@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: 2

🧹 Nitpick comments (4)
healer.lic (3)

648-663: Consider logging hook errors for debugging instead of silently swallowing them.

The rescue block at lines 655-656 silently swallows all errors, which is correct for preventing client crashes. However, this makes debugging difficult when hooks fail silently.

Consider logging errors when debug mode is enabled:

💡 Suggested improvement
     DownstreamHook.add(`@hook_id`, proc { |line|
       begin
         clean = line.gsub(/<[^>]*>/, '').strip
         queue_from_line(clean) unless clean.empty?
-      rescue StandardError
+      rescue StandardError => e
         # Never let hook errors crash the game client
+        Lich::Messaging.msg('bold', "[Healer] Hook error: #{e.message}") if $healer_debug
       end
       line # Pass through unchanged
     })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@healer.lic` around lines 648 - 663, The rescue in start_line_monitor's
DownstreamHook.add proc currently swallows all exceptions (rescue StandardError)
for `@hook_id`, making failures invisible; change it to catch errors but
conditionally log them when debug is enabled (use whatever debug flag or logger
exists) including the exception message and backtrace and context (e.g., the
offending line and `@hook_id`), so in the proc around queue_from_line(clean) you
still prevent crashes but call the logger (or debug log) with "Line monitor hook
error" + exception.inspect/backtrace when debug mode is true; also ensure
before_dying and DownstreamHook.remove remain unchanged.

1101-1110: Blocking wait for drain message could delay other patient processing.

The loop at lines 1103-1110 blocks for up to 8 seconds waiting for the "vitality transferring" message. While the comment says the drain "typically fires ~3s after the transfer command," the 8-second timeout means this could significantly delay processing of other patients or spell slot tasks.

Consider whether this blocking behavior is acceptable given the "non-blocking state machine" design goal stated in the PR description, or if the state machine should track this asynchronously.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@healer.lic` around lines 1101 - 1110, The blocking while loop that waits up
to 8s for the drain message (using start, drain_landed, get?, pause 0.1) should
be replaced with non-blocking state tracking: remove the busy-wait and instead
record that a drain is pending (e.g., set a pending_drain timestamp/flag and
expected_deadline), continue processing other patients, and have the main event
loop or the existing message-handling path check get? (or incoming messages)
each tick to set drain_landed when the "You feel your vitality transferring"
text appears or when the pending_drain deadline expires; in short, convert the
synchronous wait into an asynchronous state transition driven by the main
loop/message handler rather than the blocking while loop.

696-701: TEACH_RESPONSES constant is defined but never used.

The TEACH_RESPONSES constant (lines 104-110) defines various teaching response patterns, but teach_character uses fput without waiting for any responses. If response handling isn't needed, consider removing the unused constant to reduce confusion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@healer.lic` around lines 696 - 701, The TEACH_RESPONSES constant is defined
but never used; either remove the unused TEACH_RESPONSES constant to avoid
confusion, or update the teach_character method to handle responses by sending
the command with fput("teach scholarship to #{character}") and then
waiting/reading incoming lines to match against TEACH_RESPONSES (use your
existing read/wait helper or pattern-matching logic) so the method consumes and
reacts to the server's reply; reference TEACH_RESPONSES, teach_character, and
fput when making the change.
spec/healer_spec.rb (1)

8-28: Consider using safer alternatives to eval for loading class definitions.

The load_lic_class helper extracts and evaluates Ruby source code using eval. While this works, it has security implications and can make debugging harder (stack traces may be confusing). Additionally, the regex /^end\s*$/ to find the class end is fragile—it will break on nested classes or modules that have their own end at column 0.

If this pattern is established in the test harness, this is acceptable, but consider documenting the limitations.

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@spec/healer_spec.rb`:
- Around line 570-587: The test assumes a :monitoring phase and multiple
transfers via Healer::VIT_TRANSFER_COUNT, but the code's state machine in
process_vitality_slot uses :prepping → :recovering (no :monitoring) and issues a
single transfer via fput("transfer #{task[:patient]} vit quick"); update the
spec to expect the current behavior: assert the task[:phase] equals :prepping
(or :recovering if the code advances further) after calling
process_vitality_slot, remove the VIT_TRANSFER_COUNT expectation and instead
assert that exactly one transfer message matching "transfer Tenuk vit quick" was
sent (or adapt to the actual fput timing if prep happens before transfer).
- Around line 344-362: The test expects a whisper via DRC.bput when a patient is
detected dead but heal_dead_patient (in healer.lic) never sends it; update
heal_dead_patient to call DRC.bput with the whisper string "whisper
<patient_name> You're dead -- get a cleric!" (matching the test's format) before
removing the patient, using the same call pattern/arguments as other DRC.bput
usages in the file so the expectation (first arg equals the whisper string,
other args left as existing placeholders) is satisfied; modify heal_dead_patient
(the method invoked by heal_patient) to emit that whisper at the appropriate
branch where dead: true is handled.

---

Nitpick comments:
In `@healer.lic`:
- Around line 648-663: The rescue in start_line_monitor's DownstreamHook.add
proc currently swallows all exceptions (rescue StandardError) for `@hook_id`,
making failures invisible; change it to catch errors but conditionally log them
when debug is enabled (use whatever debug flag or logger exists) including the
exception message and backtrace and context (e.g., the offending line and
`@hook_id`), so in the proc around queue_from_line(clean) you still prevent
crashes but call the logger (or debug log) with "Line monitor hook error" +
exception.inspect/backtrace when debug mode is true; also ensure before_dying
and DownstreamHook.remove remain unchanged.
- Around line 1101-1110: The blocking while loop that waits up to 8s for the
drain message (using start, drain_landed, get?, pause 0.1) should be replaced
with non-blocking state tracking: remove the busy-wait and instead record that a
drain is pending (e.g., set a pending_drain timestamp/flag and
expected_deadline), continue processing other patients, and have the main event
loop or the existing message-handling path check get? (or incoming messages)
each tick to set drain_landed when the "You feel your vitality transferring"
text appears or when the pending_drain deadline expires; in short, convert the
synchronous wait into an asynchronous state transition driven by the main
loop/message handler rather than the blocking while loop.
- Around line 696-701: The TEACH_RESPONSES constant is defined but never used;
either remove the unused TEACH_RESPONSES constant to avoid confusion, or update
the teach_character method to handle responses by sending the command with
fput("teach scholarship to #{character}") and then waiting/reading incoming
lines to match against TEACH_RESPONSES (use your existing read/wait helper or
pattern-matching logic) so the method consumes and reacts to the server's reply;
reference TEACH_RESPONSES, teach_character, and fput when making the change.
🪄 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: 286da425-aacb-4c77-99cf-667b4d808425

📥 Commits

Reviewing files that changed from the base of the PR and between de030be and 8d8a6bd.

📒 Files selected for processing (2)
  • healer.lic
  • spec/healer_spec.rb

Comment thread spec/healer_spec.rb
Comment thread spec/healer_spec.rb Outdated
… spec fixes

- Add whisper after dead patient healing completes, remove patient from queue
- Refactor vitality drain wait to non-blocking :awaiting_drain phase via
  DownstreamHook flag so wound/disease healing continues during the wait
- Add conditional debug logging to hook rescue
- Remove unused TEACH_RESPONSES constant
- Fix spec: process_line -> queue_from_line, remove unimplemented
  vitality_check tests, rewrite vitality state machine tests to match
  actual :start -> :prepping -> :awaiting_drain -> :recovering phases

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@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: 3

🧹 Nitpick comments (1)
spec/healer_spec.rb (1)

805-810: Exercise the missing-vh branch for real.

This example uses build_healer, which stubs validate_vh_spell to false, so it will pass even if the real validation regresses. Construct a real Healer instance here so the missing-config path is actually covered.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/healer_spec.rb` around lines 805 - 810, Replace the stubbed helper usage
so the test constructs a real Healer instance (call Healer.new with the minimal
required initialization parameters used elsewhere) instead of build_healer, and
ensure no stub is applied to validate_vh_spell so the real validation runs;
assert that with the actual config missing the 'vh' waggle_set the instance
variable `@vh_available` is false (verify via
healer.instance_variable_get(:`@vh_available`)).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@healer.lic`:
- Around line 141-146: The alternation assigned to `@allowed_characters` is
unanchored and allows substring matches; change the Regexp construction to
require an exact full-string match by anchoring the alternation (e.g. wrap the
escaped names in a non-capturing group and add start/end anchors like
\A(?:...)\z) while keeping Regexp::IGNORECASE and preserving the empty-friends
fallback (/(?!)/); update the Regexp.new call that builds `@allowed_characters`
(using friends.map { |f| Regexp.escape(f) }.join('|')) to include those anchors
so only exact friend names match.
- Around line 1236-1245: The loop over `@patients` currently calls heal_patient in
insertion order and only hits the dead-patient branch when that patient arrives,
so dead patients can be delayed; change the loop logic in the block that
iterates `@patients.values` (the code around patient_ready?, `@spell_task` check,
log and heal_patient) to first detect and handle any dead patient immediately
(e.g., check patient_dead? or equivalent on patient_data[:name] before the
normal-ready check), process that dead case (calling whatever dead-handling
routine you have) and then block/stop further processing (break/return) so dead
patients truly block all processing; ensure this dead check happens before the
`@spell_task` skip and before heal_patient is considered.
- Around line 402-409: scan_room_for_patients should not call teach_character
because that causes double-teaching; remove the teach_character(pc) invocation
from scan_room_for_patients so it only adds the patient (add_patient(pc)) and
leaves needs_teach to be handled by heal_patient's deferred path. Verify that
add_patient and heal_patient still correctly set/read the needs_teach flag so
the deferred teaching path remains the single source of truth.

---

Nitpick comments:
In `@spec/healer_spec.rb`:
- Around line 805-810: Replace the stubbed helper usage so the test constructs a
real Healer instance (call Healer.new with the minimal required initialization
parameters used elsewhere) instead of build_healer, and ensure no stub is
applied to validate_vh_spell so the real validation runs; assert that with the
actual config missing the 'vh' waggle_set the instance variable `@vh_available` is
false (verify via healer.instance_variable_get(:`@vh_available`)).
🪄 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: da19ac27-c053-4fec-bbf9-3c54e9c7fcf7

📥 Commits

Reviewing files that changed from the base of the PR and between 8d8a6bd and 1dab38e.

📒 Files selected for processing (2)
  • healer.lic
  • spec/healer_spec.rb

Comment thread healer.lic
Comment thread healer.lic
Comment thread healer.lic
- Anchor @allowed_characters with \A(?:...)\z to prevent substring
  matches from enqueuing wrong players
- Remove teach_character from scan_room_for_patients to prevent
  double-teaching (heal_patient's deferred path is the single source)
- Restore TEACH_RESPONSES and use DRC.bput in teach_character so
  the server response is consumed instead of leaking into the buffer

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@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 (1)
healer.lic (1)

1229-1231: ⚠️ Potential issue | 🟠 Major

Dead-patient preemption is still not guaranteed before normal processing.

Dead handling still occurs only when a patient reaches heal_patient in the regular pass. That means spell-slot work (Line 1229) and readiness/order gating (Line 1244) can delay dead-patient blocking behavior.

Also applies to: 1242-1251

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@healer.lic` around lines 1229 - 1231, The code currently processes spell-slot
work via process_spell_slot and then later performs heal_patient, allowing
dead-patient handling to be delayed; refactor so the dead-patient preemption
runs before any normal processing: extract the liveness/dead handling logic from
heal_patient into a small helper (e.g., ensure_patient_alive or
handle_dead_patient) and invoke that helper at the top of the main processing
path immediately before process_spell_slot and before the readiness/order gating
logic, returning/short-circuiting the normal path if the patient is dead so
spell-slot and readiness gates cannot proceed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@healer.lic`:
- Around line 651-657: The DownstreamHook callback (DownstreamHook.add /
queue_from_line) mutates `@patients` (via add_patient) from a background thread
while the main loop iterates over `@patients.values`, causing a race; decouple or
synchronize access by introducing a thread-safe handoff: create a Queue (or
similar) and have queue_from_line push parsed patient events into it, then have
the main thread consume that Queue and call add_patient (or perform updates)
from the single consumer thread; alternatively wrap all direct reads/writes to
`@patients` (add_patient and the main loop iteration) with a Mutex to serialize
access—update references in queue_from_line, add_patient, and the main loop
accordingly to use the chosen synchronization method.

---

Duplicate comments:
In `@healer.lic`:
- Around line 1229-1231: The code currently processes spell-slot work via
process_spell_slot and then later performs heal_patient, allowing dead-patient
handling to be delayed; refactor so the dead-patient preemption runs before any
normal processing: extract the liveness/dead handling logic from heal_patient
into a small helper (e.g., ensure_patient_alive or handle_dead_patient) and
invoke that helper at the top of the main processing path immediately before
process_spell_slot and before the readiness/order gating logic,
returning/short-circuiting the normal path if the patient is dead so spell-slot
and readiness gates cannot proceed.
🪄 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: 0bc7fb61-7300-4968-997d-61e5bf14ee2e

📥 Commits

Reviewing files that changed from the base of the PR and between 1dab38e and ea8cbbc.

📒 Files selected for processing (1)
  • healer.lic

Comment thread healer.lic
MahtraDR and others added 2 commits April 29, 2026 18:04
Dead patients are now healed through the normal patient loop instead of
a blocking loop that monopolizes the healer. Wounds, poison, and disease
are transferred/cured one round at a time, interleaved with other
patients. Unity link and vitality transfer are skipped for dead patients.
When perceive shows the dead patient is fully clear, whisper to get a
cleric and remove from queue.

Removes the blocking heal_dead_patient method entirely.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… part

HealthResult.wounds is keyed by severity (integer), not body part name.
The healer was treating keys as body part strings, causing:
- has_healable_wounds? always returned false (no Unity links established)
- available_heal_locations always returned all parts (never rotated)
- All wound transfers piled onto left arm instead of cycling

Also add waitrt? before link in validate_link_unity to prevent exit
when a roundtime is active at login.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@healer.lic`:
- Around line 1238-1243: clear_queue currently clears patient state but doesn't
teardown active links, which can leave linked (e.g. :touched) patients still
connected; before emptying `@patients/`@spell_queue or nulling `@spell_task`,
iterate the `@patients` list and for any patient with an active link or :touched
flag call the same link-reset used in the emergency path (fall) or the link
teardown helper used elsewhere to explicitly break each link, then proceed to
clear `@patients`, set `@spell_task` = nil and clear `@spell_queue` and log as before.
- Around line 219-236: The startup check in validate_healing_spells currently
treats only DRSpells.known_spells['Heal'] as passive healing and thus forces
Regenerate users to the healme path; update the logic so that "knows_heal" is
true if either DRSpells.known_spells['Heal'] or
DRSpells.known_spells['Regenerate'] is present (and still require
DRSpells.known_spells['Adaptive Curing'] as before), and adjust the missing
list/message to reflect which of Heal/Regenerate and Adaptive Curing are absent;
reference validate_healing_spells, DRSpells.known_spells and
active_healing_spell when making this change.
🪄 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: d23ecd9d-e172-4e9a-b356-7fd103aa7d1d

📥 Commits

Reviewing files that changed from the base of the PR and between ea8cbbc and 189f7f1.

📒 Files selected for processing (2)
  • healer.lic
  • spec/healer_spec.rb

Comment thread healer.lic
Comment thread healer.lic
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@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: 2

♻️ Duplicate comments (2)
healer.lic (2)

219-236: ⚠️ Potential issue | 🟠 Major

Recognize Regenerate as passive healing too.

active_healing_spell already treats Regenerate as a valid passive-healing buff, but this gate still only checks known_spells['Heal']. A character with Regenerate + Adaptive Curing gets forced onto healme, so the passive-healing path is unreachable for that setup.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@healer.lic` around lines 219 - 236, The validate_healing_spells check only
considers DRSpells.known_spells['Heal'] but not 'Regenerate', causing
Regenerate+Adaptive Curing users to be sent to healme; update
validate_healing_spells to also check DRSpells.known_spells['Regenerate'] (e.g.,
compute knows_regen) and treat passive healing as available when knows_heal OR
knows_regen and knows_adc; also add 'Regenerate' to the missing array/report
when applicable so the messaging and return value align with
active_healing_spell which already recognizes Regenerate.

1238-1243: ⚠️ Potential issue | 🟠 Major

Break active links before clearing queue state.

clear_queue forgets every patient and resets the spell slot, but it never tears down existing links. If this is called while someone is still linked, the script loses the bookkeeping it would need to clean that relationship up while you can keep taking incoming wounds. Call the same reset used by the emergency path (fall) before wiping the queue.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@healer.lic` around lines 1238 - 1243, The clear_queue method currently clears
`@patients`, `@spell_task`, and `@spell_queue` but does not break active links; call
the same teardown/reset logic used by the emergency path (the fall routine)
before wiping state so active links are properly broken and bookkeeping is
cleared. Modify clear_queue to invoke the fall/reset helper (or extract the
link-teardown code from fall and call that helper) prior to clearing `@patients`,
`@spell_task`, and `@spell_queue` and before sending the Lich::Messaging.msg,
ensuring any linked patient relationships are torn down the same way as the
emergency path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@healer.lic`:
- Around line 744-759: The dead-patient branch currently only marks the patient
touched and lets them fall through to the normal cooldown-driven transfer flow
(victim_health.dead -> update_patient(...)), which causes corpses to be
throttled by COOLDOWN_SECONDS; instead implement a dedicated dead-patient path:
when victim_health.dead and not fully cleared, either (A) bypass cooldown by
setting the patient's timer to Time.now - COOLDOWN_SECONDS (or nil) so
transfer_wounds/clear runs immediately, or (B) run a blocking dead-clear loop
that calls heal_self, DRC.bput(...), Lich::Messaging.msg(...),
DRC.log_window(...), remove_patient(victim, reason: :dead_healed) until the
corpse is cleared; update the code around victim_health.dead, update_patient,
transfer_wounds, heal_self and remove_patient to use one of these approaches so
dead patients are processed immediately rather than reusing the normal cooldown
path.
- Around line 1030-1045: The code unconditionally advances to :awaiting_drain
and later issues the 'cast' even if the "transfer #{task[:patient]} vit quick"
failed; change the transfer call to use DRC.bput and inspect the success/failure
match (instead of fput) so you only set `@drain_landed/`@spell_task[:phase] =
:awaiting_drain and update `@spell_task`[:drain_sent_at]/:timer when the transfer
actually succeeded, and on failure abort or requeue the healing task (clear or
set a failure phase on `@spell_task` and avoid calling fput('cast') in when
:awaiting_drain). Ensure the later timeout branch still checks `@drain_landed` and
the updated `@spell_task` state before calling fput('cast').

---

Duplicate comments:
In `@healer.lic`:
- Around line 219-236: The validate_healing_spells check only considers
DRSpells.known_spells['Heal'] but not 'Regenerate', causing Regenerate+Adaptive
Curing users to be sent to healme; update validate_healing_spells to also check
DRSpells.known_spells['Regenerate'] (e.g., compute knows_regen) and treat
passive healing as available when knows_heal OR knows_regen and knows_adc; also
add 'Regenerate' to the missing array/report when applicable so the messaging
and return value align with active_healing_spell which already recognizes
Regenerate.
- Around line 1238-1243: The clear_queue method currently clears `@patients`,
`@spell_task`, and `@spell_queue` but does not break active links; call the same
teardown/reset logic used by the emergency path (the fall routine) before wiping
state so active links are properly broken and bookkeeping is cleared. Modify
clear_queue to invoke the fall/reset helper (or extract the link-teardown code
from fall and call that helper) prior to clearing `@patients`, `@spell_task`, and
`@spell_queue` and before sending the Lich::Messaging.msg, ensuring any linked
patient relationships are torn down the same way as the emergency path.
🪄 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: e125fcee-0499-4c6f-a348-62fa8e0cbe81

📥 Commits

Reviewing files that changed from the base of the PR and between 189f7f1 and 6cc95e8.

📒 Files selected for processing (1)
  • healer.lic

Comment thread healer.lic Outdated
Comment thread healer.lic Outdated
MahtraDR and others added 2 commits May 2, 2026 20:06
Dead patients: add :dead flag to patient record. patient_ready? bypasses
COOLDOWN_SECONDS when dead=true so corpses are processed every cycle
instead of being throttled under load. Flag is cleared on resurrection.

VH transfer: switch fput to DRC.bput with TRANSFER_RESPONSES so a failed
vit transfer (patient left room) aborts immediately instead of holding
the spell slot for 8s and casting VH into nothing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@MahtraDR MahtraDR merged commit 2bf05fe into elanthia-online:main May 3, 2026
3 of 4 checks passed
@MahtraDR MahtraDR deleted the feat/healer-vitality-healing branch May 3, 2026 05:35
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