Skip to content

Simplify power functions to scalar interface#41

Merged
rbx merged 5 commits intomasterfrom
scalar-power-interface
Mar 24, 2026
Merged

Simplify power functions to scalar interface#41
rbx merged 5 commits intomasterfrom
scalar-power-interface

Conversation

@rbx
Copy link
Member

@rbx rbx commented Mar 24, 2026

The per-node power values sum linearly, so the numpy array computation over all 335 nodes reduces to two scalars: number of powered-on nodes and total used cores. Make that the interface:

  • Replace power_consumption_mwh(nodes_or_num_used: np.ndarray | int, cores_or_num_idle: np.ndarray | int, include_idle_nodes=True) with power_consumption_mwh(num_powered_nodes: int, total_used_cores: int) - a one-liner, no numpy
  • Remove _power_consumption_from_state_mwh, _used_cores_from_state, and the overloaded type union
  • Replace optional nodes/cores_available array kwargs in calculate() with required num_on_nodes: int, total_used_cores: int, and drop the old-model fallback branch
  • Callers already had the two scalars as local variables; baseline_off simply passes num_used_nodes instead of num_on_nodes - no include_idle_nodes flag needed
  • int() casts added at the two numpy boundary points (environment.py, baseline.py) to keep type annotations honest throughout

Also fixes a latent bug in _compute_bounds:
The old scalar path had the arguments reversed for the min-efficiency case:

# old - wrong: 0 powered nodes, MAX_NODES used cores (not "all idle")
cost_for_min_efficiency = power_cost(0, MAX_NODES, self.prices.MAX_PRICE)

Under the new interface the intent is explicit:

# new - correct: MAX_NODES powered, 0 used cores = all nodes idle
cost_for_min_efficiency = power_cost(MAX_NODES, 0, self.prices.MAX_PRICE)

The overloaded signature masked the error - 0 and MAX_NODES were silently interpreted as num_used_nodes and num_idle_nodes respectively, which gave a different (lower) cost than the true all-idle scenario and therefore a looser normalization floor.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 24, 2026

Warning

Rate limit exceeded

@rbx has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 9 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 49371323-a93a-4867-ae81-27bf91c22cf4

📥 Commits

Reviewing files that changed from the base of the PR and between a272de9 and 60738c7.

📒 Files selected for processing (2)
  • src/environment.py
  • src/reward_calculation.py
📝 Walkthrough

Walkthrough

Replaces array- and state-derived power/reward computations with aggregate scalar inputs: functions now accept (num_on_nodes, total_used_cores). Call sites and logging updated in reward_calculation.py, environment.py, and baseline.py; num_used_cores is explicitly cast to int and baseline power/cost computed via power_consumption_mwh(...) before logging.

Changes

Cohort / File(s) Summary
Reward calculation core
src/reward_calculation.py
Removed array-based helpers and overloads; introduced aggregate power_consumption_mwh(num_powered_nodes: int, total_used_cores: int) and simplified power_cost(...). RewardCalculator.calculate and _reward_energy_efficiency_utilization_normalized now take scalar (num_on_nodes, total_used_cores) and always compute energy/price/utilization from those aggregates.
Environment call sites
src/environment.py
Derive num_on_nodes and num_used_cores scalars (explicit int(...) cast), pass them positionally to RewardCalculator.calculate and power_consumption_mwh; step power/cost now use aggregate model.
Baseline adjustments & logging
src/baseline.py
Compute baseline_power_mwh / baseline_power_off_mwh via power_consumption_mwh(num_on_nodes, num_used_cores) and derive costs by multiplying with current_price; moved env_print(...) calls to after these calculations.
Tests: deterministic/exit behavior
test/test_price_history.py, test/test_prices_cycling.py
Tests switched to assert-based failures and ensure non-zero exit on failure: test_price_history now uses sys.exit(1) on failure; cycling/determinism tests replace conditional prints with assert checks and clearer failure messages.

Sequence Diagram(s)

sequenceDiagram
    participant Env as Environment
    participant RC as RewardCalculator
    participant PC as power_consumption_mwh
    participant PCost as power_cost

    Env->>RC: calculate(num_on_nodes, num_used_cores, current_price, env_print)
    RC->>PC: power_consumption_mwh(num_on_nodes, total_used_cores)
    PC-->>RC: power_mwh
    RC->>PCost: power_cost(num_on_nodes, total_used_cores, current_price)
    PCost-->>RC: cost
    RC-->>Env: (reward, energy_cost, utilization_reward, price_reward, ...)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Pr1 reward #40: Modifies reward_calculation.py power/accounting logic — touches the same functions and may conflict with aggregate-vs-array approaches.
  • RewardCalculator #6: Refactors reward logic and RewardCalculator.calculate call sites to use aggregate node/core inputs.
  • Pr1 price reward #31: Adjusts RewardCalculator interfaces and internal reward methods to accept aggregated counts, aligning with this PR’s signature updates.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and concisely summarizes the main change: simplifying power functions to use a scalar interface instead of array-based parameters.
Description check ✅ Passed The description is highly relevant, providing detailed context about why the change was made, the specific interface modifications, and a latent bug fix.
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%.

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


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
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

🧹 Nitpick comments (2)
src/environment.py (1)

480-485: Keep step_cost single-sourced.

calculate() already returns the cost for these exact aggregates, but Line 490 overwrites it with a second copy of the formula. Reusing the returned value here will keep the reward path and metrics path from drifting if the cost model changes again.

♻️ Proposed cleanup
         step_reward, step_cost, eff_reward_norm, price_reward, idle_penalty_norm, job_age_penalty_norm = self.reward_calculator.calculate(
             num_used_nodes, num_idle_nodes, current_price, average_future_price,
             num_off_nodes, num_launched_jobs, num_node_changes, job_queue_2d,
             num_unprocessed_jobs, self.weights, num_dropped_this_step, self.env_print,
             num_on_nodes, num_used_cores,
         )
 
         self.metrics.episode_reward += step_reward
         step_power_mwh = power_consumption_mwh(num_on_nodes, num_used_cores)
-        
-        step_cost = step_power_mwh * current_price

Also applies to: 488-490

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

In `@src/environment.py` around lines 480 - 485, The code recomputes the step cost
after calling self.reward_calculator.calculate(...) which already returns
step_cost; remove the second formula and reuse the step_cost value returned by
reward_calculator.calculate so the reward and metrics paths stay consistent.
Locate the call to self.reward_calculator.calculate(...) and any subsequent
reassignment/explicit recomputation of step_cost (or a local variable named
cost) and replace that recomputation by using the step_cost variable returned
from calculate() wherever the cost is needed (metrics, logging, or downstream
reward math).
src/reward_calculation.py (1)

82-89: Trim the retired efficiency-bounds path or align it with the live reward.

calculate() now uses _reward_energy_efficiency_utilization_normalized(), so these price-dependent _min_efficiency_reward / _max_efficiency_reward values no longer influence the active reward path in this file. Keeping _compute_bounds() wired to the old formula makes future tuning harder to reason about.

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

In `@src/reward_calculation.py` around lines 82 - 89, _compute_bounds currently
computes _min_efficiency_reward and _max_efficiency_reward using the old
_reward_efficiency path, but calculate() now uses
_reward_energy_efficiency_utilization_normalized; update _compute_bounds to
either remove these retired bounds or recompute them using
_reward_energy_efficiency_utilization_normalized (e.g., call
_reward_energy_efficiency_utilization_normalized for the extreme cases used
before: idle vs fully utilized with MIN_PRICE/MAX_PRICE or corresponding utility
inputs) so the stored _min_efficiency_reward/_max_efficiency_reward reflect the
live reward path (or delete their assignments if they are no longer referenced
anywhere). Ensure you modify the assignments to _min_efficiency_reward and
_max_efficiency_reward inside _compute_bounds and keep references to MAX_NODES,
CORES_PER_NODE and prices as needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/reward_calculation.py`:
- Around line 16-31: The power_consumption_mwh helper currently accepts invalid
scalar combinations (negative num_powered_nodes, negative total_used_cores, or
total_used_cores > num_powered_nodes * CORES_PER_NODE) and returns bogus values;
add input validation at the start of power_consumption_mwh to raise ValueError
on these impossible states. Specifically, in power_consumption_mwh check that
num_powered_nodes and total_used_cores are >= 0 and that total_used_cores <=
num_powered_nodes * CORES_PER_NODE, and if any check fails raise a clear
exception message referencing the offending values and the constants
(COST_IDLE_MW, COST_USED_MW, CORES_PER_NODE) so callers fail fast instead of
producing skewed energy/cost metrics.

---

Nitpick comments:
In `@src/environment.py`:
- Around line 480-485: The code recomputes the step cost after calling
self.reward_calculator.calculate(...) which already returns step_cost; remove
the second formula and reuse the step_cost value returned by
reward_calculator.calculate so the reward and metrics paths stay consistent.
Locate the call to self.reward_calculator.calculate(...) and any subsequent
reassignment/explicit recomputation of step_cost (or a local variable named
cost) and replace that recomputation by using the step_cost variable returned
from calculate() wherever the cost is needed (metrics, logging, or downstream
reward math).

In `@src/reward_calculation.py`:
- Around line 82-89: _compute_bounds currently computes _min_efficiency_reward
and _max_efficiency_reward using the old _reward_efficiency path, but
calculate() now uses _reward_energy_efficiency_utilization_normalized; update
_compute_bounds to either remove these retired bounds or recompute them using
_reward_energy_efficiency_utilization_normalized (e.g., call
_reward_energy_efficiency_utilization_normalized for the extreme cases used
before: idle vs fully utilized with MIN_PRICE/MAX_PRICE or corresponding utility
inputs) so the stored _min_efficiency_reward/_max_efficiency_reward reflect the
live reward path (or delete their assignments if they are no longer referenced
anywhere). Ensure you modify the assignments to _min_efficiency_reward and
_max_efficiency_reward inside _compute_bounds and keep references to MAX_NODES,
CORES_PER_NODE and prices as needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: caec7276-9770-4135-a9b4-13b3d85e0d4e

📥 Commits

Reviewing files that changed from the base of the PR and between c31e453 and da3478a.

📒 Files selected for processing (3)
  • src/baseline.py
  • src/environment.py
  • src/reward_calculation.py

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 24, 2026

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":500,"request":{"method":"PATCH","url":"https://api.github.com/repos/FairRootGroup/powersched/issues/comments/4118585808","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n<details>\n<summary>📝 Walkthrough</summary>\n\n## Walkthrough\n\nThe changes refactor power and cost calculations from array-based node/core state tracking to scalar-based accounting using `num_powered_nodes` and `total_used_cores`. Function signatures in `reward_calculation.py` are simplified, with corresponding updates to callers in `baseline.py` and `environment.py`.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Power Calculation Refactor** <br> `src/reward_calculation.py`|Removed array-based functions (`_power_consumption_from_state_mwh`, `_used_cores_from_state`) and updated `power_consumption_mwh`, `power_cost`, `_reward_energy_efficiency_utilization_normalized`, and `calculate` to accept scalar `num_powered_nodes` and `total_used_cores` instead of node/core arrays. Updated power formula to compute cost as `num_powered_nodes * COST_IDLE_MW + (COST_USED_MW - COST_IDLE_MW) * total_used_cores / CORES_PER_NODE`. Removed conditional branch selecting between utilization-based vs. aggregate calculation modes.|\n|**Calculation Integration** <br> `src/baseline.py`, `src/environment.py`|Updated calls to `power_consumption_mwh` and reward calculator methods to pass derived scalar values (`num_on_nodes`, `num_used_cores`) instead of node state and cores availability arrays. Added explicit integer casting for `num_used_cores`. Updated logging placement in baseline calculations.|\n\n## Estimated code review effort\n\n🎯 3 (Moderate) | ⏱️ ~20 minutes\n\n## Possibly related PRs\n\n- FairRootGroup/powersched#31: Modifies `RewardCalculator` parameterization to use node-count-based inputs (`num_on_nodes`/`num_used_nodes`) instead of array-based state.\n- FairRootGroup/powersched#6: Updates reward calculation machinery and `RewardCalculator.calculate()` signature to use aggregate `num_on_nodes` and `total_used_cores` parameters.\n- FairRootGroup/powersched#40: Refactors array-to-scalar accounting in power and usage logic, operating on the same calculation functions and reward mechanics.\n\n</details>\n\n<!-- walkthrough_end -->\n\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 3</summary>\n\n<details>\n<summary>✅ Passed checks (3 passed)</summary>\n\n|     Check name     | Status   | Explanation                                                                                                                                                                                                                          |\n| :----------------: | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n|     Title check    | ✅ Passed | The title 'Simplify power functions to scalar interface' directly and accurately summarizes the main change: replacing array-based power computation with a simpler scalar-based interface.                                          |\n| Docstring Coverage | ✅ Passed | Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.                                                                                                                                                  |\n|  Description check | ✅ Passed | The pull request description clearly explains the rationale for simplifying the power interface from array-based to scalar-based computations, cites the linear summation principle, and documents the specific changes and bug fix. |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- tips_start -->\n\n---\n\nThanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=FairRootGroup/powersched&utm_content=41)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.\n\n<details>\n<summary>❤️ Share</summary>\n\n- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)\n- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)\n- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)\n- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)\n\n</details>\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{"retryCount":3,"retries":3,"retryAfter":16}}}

Copy link
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.

🧹 Nitpick comments (1)
src/environment.py (1)

480-485: Pass the new scalar pair by keyword.

These last two arguments are both plain integers, and this PR already fixed one silent reversal in the scalar power path. Keywords would make this call self-documenting and harder to swap accidentally.

♻️ Suggested call-site tweak
         step_reward, step_cost, eff_reward_norm, price_reward, idle_penalty_norm, job_age_penalty_norm = self.reward_calculator.calculate(
             num_used_nodes, num_idle_nodes, current_price, average_future_price,
             num_off_nodes, num_launched_jobs, num_node_changes, job_queue_2d,
             num_unprocessed_jobs, self.weights, num_dropped_this_step, self.env_print,
-            num_on_nodes, num_used_cores,
+            num_on_nodes=num_on_nodes,
+            total_used_cores=num_used_cores,
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/environment.py` around lines 480 - 485, The call to
reward_calculator.calculate currently passes the last two integer arguments
positionally (num_on_nodes, num_used_cores) which makes them easy to swap;
update the call site in the block where step_reward,... =
self.reward_calculator.calculate(...) to pass those two as keywords (e.g.
num_on_nodes=num_on_nodes, num_used_cores=num_used_cores) so the meaning is
explicit and accidental reordering is prevented while keeping all other
positional args the same.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/environment.py`:
- Around line 480-485: The call to reward_calculator.calculate currently passes
the last two integer arguments positionally (num_on_nodes, num_used_cores) which
makes them easy to swap; update the call site in the block where step_reward,...
= self.reward_calculator.calculate(...) to pass those two as keywords (e.g.
num_on_nodes=num_on_nodes, num_used_cores=num_used_cores) so the meaning is
explicit and accidental reordering is prevented while keeping all other
positional args the same.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b108ecc6-7fd7-4db2-b398-cb448e8c0835

📥 Commits

Reviewing files that changed from the base of the PR and between da3478a and 0ed3b30.

📒 Files selected for processing (1)
  • src/environment.py

rbx added 3 commits March 24, 2026 18:26
The per-node power values sum linearly, so the numpy array computation
reduces to two scalars. Replace the overloaded array/int signatures with
power_consumption_mwh(num_powered_nodes: int, total_used_cores: int).

- Remove _power_consumption_from_state_mwh and _used_cores_from_state
- Remove overloaded np.ndarray | int type union
- Replace optional nodes/cores_available kwargs in calculate() with
  required num_on_nodes/total_used_cores; drop old-model fallback branch
- baseline_off passes num_used_nodes instead of num_on_nodes — no
  include_idle_nodes flag needed
- int() casts at numpy boundary points (environment.py, baseline.py)
calculate() already returns total_cost as its second value; the
immediately following step_cost = step_power_mwh * current_price
was recomputing the same value from the same inputs.
@rbx rbx force-pushed the scalar-power-interface branch from 0ed3b30 to f17da87 Compare March 24, 2026 17:30
Copy link
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.

🧹 Nitpick comments (1)
src/reward_calculation.py (1)

256-266: Redundant variable assignment.

Line 260 used_cores = total_used_cores is unnecessary — total_used_cores can be used directly in the subsequent logic.

♻️ Suggested simplification
         step_power_mwh = power_consumption_mwh(num_on_nodes, total_used_cores)
         if step_power_mwh <= 0.0:
             return 0.0
 
-        used_cores = total_used_cores
-        if used_cores <= 0.0:
+        if total_used_cores <= 0:
             efficiency_ratio = 0.0
         else:
-            efficiency_raw = used_cores / step_power_mwh  # core-hours per MWh for this 1h step
+            efficiency_raw = total_used_cores / step_power_mwh  # core-hours per MWh for this 1h step
             efficiency_max = float(CORES_PER_NODE) / COST_USED_MW
             efficiency_ratio = float(np.clip(efficiency_raw / efficiency_max, 0.0, 1.0))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/reward_calculation.py` around lines 256 - 266, The assignment used_cores
= total_used_cores is redundant; remove the local used_cores variable and use
total_used_cores directly in the conditional and computation (replace used_cores
<= 0.0 check and efficiency_raw = used_cores / step_power_mwh with checks and
calculation against total_used_cores), keeping the rest of the logic intact
(functions/symbols: power_consumption_mwh, step_power_mwh, total_used_cores,
efficiency_raw, efficiency_max, np.clip, CORES_PER_NODE, COST_USED_MW).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/reward_calculation.py`:
- Around line 256-266: The assignment used_cores = total_used_cores is
redundant; remove the local used_cores variable and use total_used_cores
directly in the conditional and computation (replace used_cores <= 0.0 check and
efficiency_raw = used_cores / step_power_mwh with checks and calculation against
total_used_cores), keeping the rest of the logic intact (functions/symbols:
power_consumption_mwh, step_power_mwh, total_used_cores, efficiency_raw,
efficiency_max, np.clip, CORES_PER_NODE, COST_USED_MW).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 36f0ca32-5920-48ef-826c-fde5e67165da

📥 Commits

Reviewing files that changed from the base of the PR and between 0ed3b30 and f17da87.

📒 Files selected for processing (5)
  • src/baseline.py
  • src/environment.py
  • src/reward_calculation.py
  • test/test_price_history.py
  • test/test_prices_cycling.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/environment.py
  • src/baseline.py

@rbx rbx merged commit fa448ed into master Mar 24, 2026
4 checks passed
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