feat(healer): vitality healing, spell slot mutex, dead detection#7330
Conversation
…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>
|
@coderabbitai review this pr in full |
|
✅ Actions performedFull review triggered. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughPersistent 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. ChangesCore Healer
Test Suite
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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.
Built for teams:
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. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
There was a problem hiding this comment.
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_RESPONSESconstant is defined but never used.The
TEACH_RESPONSESconstant (lines 104-110) defines various teaching response patterns, butteach_characterusesfputwithout 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 toevalfor loading class definitions.The
load_lic_classhelper extracts and evaluates Ruby source code usingeval. 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 ownendat 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
📒 Files selected for processing (2)
healer.licspec/healer_spec.rb
… 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>
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
spec/healer_spec.rb (1)
805-810: Exercise the missing-vhbranch for real.This example uses
build_healer, which stubsvalidate_vh_spelltofalse, so it will pass even if the real validation regresses. Construct a realHealerinstance 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
📒 Files selected for processing (2)
healer.licspec/healer_spec.rb
- 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>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
healer.lic (1)
1229-1231:⚠️ Potential issue | 🟠 MajorDead-patient preemption is still not guaranteed before normal processing.
Dead handling still occurs only when a patient reaches
heal_patientin 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
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>
There was a problem hiding this comment.
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
📒 Files selected for processing (2)
healer.licspec/healer_spec.rb
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
healer.lic (2)
219-236:⚠️ Potential issue | 🟠 MajorRecognize
Regenerateas passive healing too.
active_healing_spellalready treatsRegenerateas a valid passive-healing buff, but this gate still only checksknown_spells['Heal']. A character withRegenerate+Adaptive Curinggets forced ontohealme, 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 | 🟠 MajorBreak active links before clearing queue state.
clear_queueforgets 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
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>
Summary
healer.licwith parallel patient processing and non-blocking spell slot state machinesbput/fputcalls during patient processingvhwaggle_set), with prep-first approach and reactive VH castingissue_command-- hard exit if not availablevhwaggle_set is missingArchitecture
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
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests